Boot splash and more (#98)

* Boot splash and more

- Added developer sdkconfig
- Refactored the way FreeRTOS includes are included
- Improved Gui/Loader logic
- Implemented boot app with splash screen

* Updated naming for Gui and Loader services

* Renamed Screenshot service methods

* Renames

* Service renames
This commit is contained in:
Ken Van Hoeylandt
2024-11-30 15:37:16 +01:00
committed by GitHub
parent 3f62ec2efa
commit 0188ce721c
60 changed files with 726 additions and 307 deletions
+2
View File
@@ -12,6 +12,8 @@ namespace tt::app {
class App;
typedef enum {
/** Boot screen, shown before desktop is launched. */
TypeBoot,
/** A desktop app sits at the root of the app stack managed by the Loader service */
TypeDesktop,
/** Apps that generally aren't started from the desktop (e.g. image viewer) */
+73
View File
@@ -0,0 +1,73 @@
#include <Timer.h>
#include <Check.h>
#include <Thread.h>
#include <Kernel.h>
#include "Assets.h"
#include "app/App.h"
#include "lvgl.h"
#include "service/loader/Loader.h"
#include "lvgl/Style.h"
#ifdef ESP_PLATFORM
#include "sdkconfig.h"
#else
#define CONFIG_TT_SPLASH_DURATION 0
#endif
namespace tt::app::boot {
static int32_t threadCallback(void* context);
struct Data {
Data() : thread("", 4096, threadCallback, this) {}
Thread thread;
};
static int32_t threadCallback(TT_UNUSED void* context) {
TickType_t start_time = tt::get_ticks();
// Do stuff
TickType_t end_time = tt::get_ticks();
TickType_t ticks_passed = end_time - start_time;
TickType_t minimum_ticks = (CONFIG_TT_SPLASH_DURATION / portTICK_PERIOD_MS);
if (minimum_ticks > ticks_passed) {
tt::delay_ticks(minimum_ticks - ticks_passed);
}
tt::service::loader::stopApp();
tt::service::loader::startApp("Desktop");
return 0;
}
static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
Data* data = (Data*)app.getData();
lv_obj_t* image = lv_image_create(parent);
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
lv_image_set_src(image, TT_ASSETS_BOOT_LOGO);
lvgl::obj_set_style_bg_blacken(parent);
data->thread.start();
}
static void onStart(App& app) {
Data* data = new Data();
app.setData(data);
}
static void onStop(App& app) {
Data* data = (Data*)app.getData();
data->thread.join();
tt_assert(data);
delete data;
}
extern const Manifest manifest = {
.id = "Boot",
.name = "Boot",
.type = TypeBoot,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
};
} // namespace
+1 -1
View File
@@ -11,7 +11,7 @@ static void on_app_pressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
service::loader::start_app(manifest->id, false, Bundle());
service::loader::startApp(manifest->id, false, Bundle());
}
}
+5 -5
View File
@@ -72,7 +72,7 @@ static void on_navigate_up_pressed(lv_event_t* event) {
}
static void on_exit_app_pressed(TT_UNUSED lv_event_t* event) {
service::loader::stop_app();
service::loader::stopApp();
}
static void view_file(const char* path, const char* filename) {
@@ -84,7 +84,7 @@ static void view_file(const char* path, const char* filename) {
// For PC we need to make the path relative to the current work directory,
// because that's how LVGL maps its 'drive letter' to the file system.
char* processed_filepath;
if (get_platform() == PlatformPc) {
if (get_platform() == PlatformSimulator) {
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) == nullptr) {
TT_LOG_E(TAG, "Failed to get current working directory");
@@ -105,7 +105,7 @@ static void view_file(const char* path, const char* filename) {
if (is_supported_image_file(filename)) {
Bundle bundle;
bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
service::loader::start_app("ImageViewer", false, bundle);
service::loader::startApp("ImageViewer", false, bundle);
} else if (is_supported_text_file(filename)) {
Bundle bundle;
if (get_platform() == PlatformEsp) {
@@ -114,7 +114,7 @@ static void view_file(const char* path, const char* filename) {
// Remove forward slash, because we need a relative path
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1);
}
service::loader::start_app("TextViewer", false, bundle);
service::loader::startApp("TextViewer", false, bundle);
} else {
TT_LOG_W(TAG, "opening files of this type is not supported");
}
@@ -199,7 +199,7 @@ static void on_show(App& app, lv_obj_t* parent) {
static void on_start(App& app) {
auto* data = data_alloc();
// PC platform is bound to current work directory because of the LVGL file system mapping
if (get_platform() == PlatformPc) {
if (get_platform() == PlatformSimulator) {
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != nullptr) {
data_set_entries_for_path(data, cwd);
@@ -12,7 +12,7 @@ namespace tt::app::screenshot {
static void update_mode(ScreenshotUi* ui) {
lv_obj_t* label = ui->start_stop_button_label;
if (service::screenshot::is_started()) {
if (service::screenshot::isStarted()) {
lv_label_set_text(label, "Stop");
} else {
lv_label_set_text(label, "Start");
@@ -34,7 +34,7 @@ static void on_mode_set(lv_event_t* event) {
static void on_start_pressed(lv_event_t* event) {
auto* ui = static_cast<ScreenshotUi*>(lv_event_get_user_data(event));
if (service::screenshot::is_started()) {
if (service::screenshot::isStarted()) {
TT_LOG_I(TAG, "Stop screenshot");
service::screenshot::stop();
} else {
@@ -45,13 +45,13 @@ static void on_start_pressed(lv_event_t* event) {
const char* delay_text = lv_textarea_get_text(ui->delay_textarea);
int delay = atoi(delay_text);
if (delay > 0) {
service::screenshot::start_timed(path, delay, 1);
service::screenshot::startTimed(path, delay, 1);
} else {
TT_LOG_W(TAG, "Ignored screenshot start because delay was 0");
}
} else {
TT_LOG_I(TAG, "Start app screenshots");
service::screenshot::start_apps(path);
service::screenshot::startApps(path);
}
}
@@ -73,7 +73,7 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
lv_obj_align_to(mode_dropdown, mode_label, LV_ALIGN_OUT_RIGHT_MID, 8, 0);
lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, ui);
ui->mode_dropdown = mode_dropdown;
service::screenshot::ScreenshotMode mode = service::screenshot::get_mode();
service::screenshot::Mode mode = service::screenshot::getMode();
if (mode == service::screenshot::ScreenshotModeApps) {
lv_dropdown_set_selected(mode_dropdown, 1);
}
@@ -168,8 +168,8 @@ void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent) {
create_path_ui(ui, wrapper);
create_timer_settings_ui(ui, wrapper);
service::gui::keyboard_add_textarea(ui->delay_textarea);
service::gui::keyboard_add_textarea(ui->path_textarea);
service::gui::keyboardAddTextArea(ui->delay_textarea);
service::gui::keyboardAddTextArea(ui->path_textarea);
update_mode(ui);
}
@@ -49,11 +49,11 @@ static void onListItemSelected(lv_event_t* e) {
if (code == LV_EVENT_CLICKED) {
size_t index = (size_t)(e->user_data);
TT_LOG_I(TAG, "Selected item at index %d", index);
tt::app::App* app = service::loader::get_current_app();
tt::app::App* app = service::loader::getCurrentApp();
Bundle bundle;
setResultIndex(bundle, (int32_t)index);
app->setResult(app::ResultOk, bundle);
service::loader::stop_app();
service::loader::stopApp();
}
}
@@ -79,12 +79,12 @@ static void onShow(App& app, lv_obj_t* parent) {
if (items.empty() || items.front().empty()) {
TT_LOG_E(TAG, "No items provided");
app.setResult(ResultError);
service::loader::stop_app();
service::loader::stopApp();
} else if (items.size() == 1) {
Bundle result_bundle;
setResultIndex(result_bundle, 0);
app.setResult(ResultOk, result_bundle);
service::loader::stop_app();
service::loader::stopApp();
TT_LOG_W(TAG, "Auto-selecting single item");
} else {
size_t index = 0;
@@ -95,7 +95,7 @@ static void onShow(App& app, lv_obj_t* parent) {
} else {
TT_LOG_E(TAG, "No items provided");
app.setResult(ResultError);
service::loader::stop_app();
service::loader::stopApp();
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ static void on_app_pressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
service::loader::start_app(manifest->id, false, Bundle());
service::loader::startApp(manifest->id);
}
}
@@ -24,7 +24,7 @@ static void on_connect(const service::wifi::settings::WifiApSettings* ap_setting
static WifiConnect* wifi_connect_alloc() {
auto* wifi = static_cast<WifiConnect*>(malloc(sizeof(WifiConnect)));
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &event_callback, wifi);
wifi->mutex = tt_mutex_alloc(MutexTypeNormal);
wifi->state = (WifiConnectState) {
@@ -46,7 +46,7 @@ static WifiConnect* wifi_connect_alloc() {
}
static void wifi_connect_free(WifiConnect* wifi) {
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
tt_mutex_free(wifi->mutex);
@@ -92,7 +92,7 @@ static void event_callback(const void* message, void* context) {
case service::wifi::WifiEventTypeConnectionSuccess:
if (wifi->state.is_connecting) {
state_set_connecting(wifi, false);
service::loader::stop_app();
service::loader::stopApp();
}
break;
default:
@@ -191,8 +191,8 @@ void view_create(const App& app, void* wifi, lv_obj_t* parent) {
view_create_bottom_buttons(wifi_connect, wrapper);
// Keyboard bindings
service::gui::keyboard_add_textarea(view->ssid_textarea);
service::gui::keyboard_add_textarea(view->password_textarea);
service::gui::keyboardAddTextArea(view->ssid_textarea);
service::gui::keyboardAddTextArea(view->password_textarea);
// Init from app parameters
const Bundle& bundle = app.getParameters();
+13 -13
View File
@@ -26,7 +26,7 @@ static void on_connect(const char* ssid) {
Bundle bundle;
bundle.putString(WIFI_CONNECT_PARAM_SSID, ssid);
bundle.putString(WIFI_CONNECT_PARAM_PASSWORD, "");
service::loader::start_app("WifiConnect", false, bundle);
service::loader::startApp("WifiConnect", false, bundle);
}
}
@@ -35,7 +35,7 @@ static void on_disconnect() {
}
static void on_wifi_toggled(bool enabled) {
service::wifi::set_enabled(enabled);
service::wifi::setEnabled(enabled);
}
static WifiManage* wifi_manage_alloc() {
@@ -44,8 +44,8 @@ static WifiManage* wifi_manage_alloc() {
wifi->wifi_subscription = nullptr;
wifi->mutex = tt_mutex_alloc(MutexTypeNormal);
wifi->state = (WifiManageState) {
.scanning = service::wifi::is_scanning(),
.radio_state = service::wifi::get_radio_state(),
.scanning = service::wifi::isScanning(),
.radio_state = service::wifi::getRadioState(),
.connect_ssid = { 0 },
.ap_records = { },
.ap_records_count = 0
@@ -94,8 +94,8 @@ void request_view_update(WifiManage* wifi) {
static void wifi_manage_event_callback(const void* message, void* context) {
auto* event = (service::wifi::WifiEvent*)message;
auto* wifi = (WifiManage*)context;
TT_LOG_I(TAG, "Update with state %d", service::wifi::get_radio_state());
state_set_radio_state(wifi, service::wifi::get_radio_state());
TT_LOG_I(TAG, "Update with state %d", service::wifi::getRadioState());
state_set_radio_state(wifi, service::wifi::getRadioState());
switch (event->type) {
case tt::service::wifi::WifiEventTypeScanStarted:
state_set_scanning(wifi, true);
@@ -105,7 +105,7 @@ static void wifi_manage_event_callback(const void* message, void* context) {
state_update_scanned_records(wifi);
break;
case tt::service::wifi::WifiEventTypeRadioStateOn:
if (!service::wifi::is_scanning()) {
if (!service::wifi::isScanning()) {
service::wifi::scan();
}
break;
@@ -119,12 +119,12 @@ static void wifi_manage_event_callback(const void* message, void* context) {
static void app_show(App& app, lv_obj_t* parent) {
auto* wifi = (WifiManage*)app.getData();
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_manage_event_callback, wifi);
// State update (it has its own locking)
state_set_radio_state(wifi, service::wifi::get_radio_state());
state_set_scanning(wifi, service::wifi::is_scanning());
state_set_radio_state(wifi, service::wifi::getRadioState());
state_set_scanning(wifi, service::wifi::isScanning());
state_update_scanned_records(wifi);
// View update
@@ -135,11 +135,11 @@ static void app_show(App& app, lv_obj_t* parent) {
view_update(&wifi->view, &wifi->bindings, &wifi->state);
unlock(wifi);
service::wifi::WifiRadioState radio_state = service::wifi::get_radio_state();
service::wifi::WifiRadioState radio_state = service::wifi::getRadioState();
bool can_scan = radio_state == service::wifi::WIFI_RADIO_ON ||
radio_state == service::wifi::WIFI_RADIO_CONNECTION_PENDING ||
radio_state == service::wifi::WIFI_RADIO_CONNECTION_ACTIVE;
if (can_scan && !service::wifi::is_scanning()) {
if (can_scan && !service::wifi::isScanning()) {
service::wifi::scan();
}
}
@@ -147,7 +147,7 @@ static void app_show(App& app, lv_obj_t* parent) {
static void app_hide(App& app) {
auto* wifi = (WifiManage*)app.getData();
lock(wifi);
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
wifi->wifi_subscription = nullptr;
wifi->view_enabled = false;
@@ -16,7 +16,7 @@ void state_set_radio_state(WifiManage* wifi, service::wifi::WifiRadioState state
void state_update_scanned_records(WifiManage* wifi) {
lock(wifi);
service::wifi::get_scan_results(
service::wifi::getScanResults(
wifi->state.ap_records,
WIFI_SCAN_AP_RECORD_COUNT,
&wifi->state.ap_records_count
@@ -43,7 +43,7 @@ static void connect(lv_event_t* event) {
static void create_network_button(WifiManageView* view, WifiManageBindings* bindings, service::wifi::WifiApRecord* record) {
const char* ssid = (const char*)record->ssid;
const char* icon = service::statusbar::get_status_icon_for_rssi(record->rssi, record->auth_mode != WIFI_AUTH_OPEN);
const char* icon = service::statusbar::getWifiStatusIconForRssi(record->rssi, record->auth_mode != WIFI_AUTH_OPEN);
lv_obj_t* ap_button = lv_list_add_btn(
view->networks_list,
icon,