mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-19 01:45:06 +00:00
App and Service improvements (#106)
This commit is contained in:
parent
e86a11b7e2
commit
50ee77d572
@ -1,8 +1,8 @@
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
|
||||
static void onShow(tt::app::App& app, lv_obj_t* parent) {
|
||||
lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app);
|
||||
static void onShow(tt::app::AppContext& context, lv_obj_t* parent) {
|
||||
lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, context);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
lv_obj_t* label = lv_label_create(parent);
|
||||
@ -10,7 +10,7 @@ static void onShow(tt::app::App& app, lv_obj_t* parent) {
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
extern const tt::app::Manifest hello_world_app = {
|
||||
extern const tt::app::AppManifest hello_world_app = {
|
||||
.id = "HelloWorld",
|
||||
.name = "Hello World",
|
||||
.onShow = onShow,
|
||||
|
||||
@ -7,7 +7,7 @@ namespace tt::service::wifi {
|
||||
extern void wifi_main(void*);
|
||||
}
|
||||
|
||||
extern const tt::app::Manifest hello_world_app;
|
||||
extern const tt::app::AppManifest hello_world_app;
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
@ -38,9 +38,9 @@ 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!
|
||||
|
||||
```C++
|
||||
static void onShow(tt::app::App app, lv_obj_t* parent) {
|
||||
static void onShow(tt::app::AppContext context, lv_obj_t* parent) {
|
||||
// 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, context);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
// Label widget
|
||||
@ -50,7 +50,7 @@ static void onShow(tt::app::App app, lv_obj_t* parent) {
|
||||
// Widgets are auto-removed from the parent when the app is closed
|
||||
}
|
||||
|
||||
extern const tt::app::Manifest manifest = {
|
||||
extern const tt::app::AppManifest manifest = {
|
||||
.id = "HelloWorld", // Used to identify and start an app
|
||||
.name = "Hello World", // Shown on the desktop and app's toolbar
|
||||
.onShow = onShow // A minimal setup sets the onShow() function
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/Manifest.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "Bundle.h"
|
||||
#include "Mutex.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
@ -17,70 +18,70 @@ typedef enum {
|
||||
} State;
|
||||
|
||||
struct ResultHolder {
|
||||
ResultHolder(Result result, const Bundle& resultData) {
|
||||
this->result = result;
|
||||
this->resultData = new Bundle(resultData);
|
||||
}
|
||||
|
||||
~ResultHolder() {
|
||||
delete resultData;
|
||||
}
|
||||
|
||||
Result result;
|
||||
Bundle* _Nullable resultData;
|
||||
std::shared_ptr<const Bundle> resultData;
|
||||
|
||||
explicit ResultHolder(Result result) : result(result), resultData(nullptr) {}
|
||||
|
||||
ResultHolder(Result result, std::shared_ptr<const Bundle> resultData) :
|
||||
result(result),
|
||||
resultData(std::move(resultData))
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread-safe app instance.
|
||||
*/
|
||||
class AppInstance : public App {
|
||||
class AppInstance : public AppContext {
|
||||
|
||||
private:
|
||||
|
||||
Mutex mutex = Mutex(MutexTypeNormal);
|
||||
const Manifest& manifest;
|
||||
const AppManifest& manifest;
|
||||
State state = StateInitial;
|
||||
Flags flags = { .showStatusbar = true };
|
||||
/** @brief Optional parameters to start the app with
|
||||
* When these are stored in the app struct, the struct takes ownership.
|
||||
* Do not mutate after app creation.
|
||||
*/
|
||||
tt::Bundle parameters;
|
||||
std::shared_ptr<const tt::Bundle> _Nullable parameters;
|
||||
/** @brief @brief Contextual data related to the running app's instance
|
||||
* The app can attach its data to this.
|
||||
* The lifecycle is determined by the on_start and on_stop methods in the AppManifest.
|
||||
* These manifest methods can optionally allocate/free data that is attached here.
|
||||
*/
|
||||
void* _Nullable data = nullptr;
|
||||
std::unique_ptr<ResultHolder> resultHolder;
|
||||
std::shared_ptr<void> _Nullable data;
|
||||
std::unique_ptr<ResultHolder> _Nullable resultHolder;
|
||||
|
||||
public:
|
||||
|
||||
AppInstance(const Manifest& manifest) :
|
||||
explicit AppInstance(const AppManifest& manifest) :
|
||||
manifest(manifest) {}
|
||||
|
||||
AppInstance(const Manifest& manifest, const Bundle& parameters) :
|
||||
AppInstance(const AppManifest& manifest, std::shared_ptr<const Bundle> parameters) :
|
||||
manifest(manifest),
|
||||
parameters(parameters) {}
|
||||
parameters(std::move(parameters)) {}
|
||||
|
||||
~AppInstance() {}
|
||||
~AppInstance() override = default;
|
||||
|
||||
void setState(State state);
|
||||
State getState() const;
|
||||
[[nodiscard]] State getState() const;
|
||||
|
||||
const Manifest& getManifest() const;
|
||||
[[nodiscard]] const AppManifest& getManifest() const override;
|
||||
|
||||
Flags getFlags() const;
|
||||
[[nodiscard]] Flags getFlags() const override;
|
||||
void setFlags(Flags flags);
|
||||
Flags& mutableFlags() { return flags; }
|
||||
Flags& mutableFlags() { return flags; } // TODO: locking mechanism
|
||||
|
||||
_Nullable void* getData() const;
|
||||
void setData(void* data);
|
||||
[[nodiscard]] std::shared_ptr<void> _Nullable getData() const override;
|
||||
void setData(std::shared_ptr<void> data) override;
|
||||
|
||||
const Bundle& getParameters() const;
|
||||
[[nodiscard]] std::shared_ptr<const Bundle> getParameters() const override;
|
||||
|
||||
void setResult(Result result, const Bundle& bundle);
|
||||
bool hasResult() const;
|
||||
void setResult(Result result) override;
|
||||
void setResult(Result result, std::shared_ptr<const Bundle> bundle) override;
|
||||
[[nodiscard]] bool hasResult() const override;
|
||||
std::unique_ptr<ResultHolder>& getResult() { return resultHolder; }
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/Manifest.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "app/AppInstance.h"
|
||||
#include "MessageQueue.h"
|
||||
#include "Pubsub.h"
|
||||
@ -9,6 +9,7 @@
|
||||
#include "service/loader/Loader.h"
|
||||
#include "RtosCompatSemaphore.h"
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
|
||||
namespace tt::service::loader {
|
||||
|
||||
@ -35,7 +36,7 @@ typedef struct {
|
||||
} LoaderEventAppHiding;
|
||||
|
||||
typedef struct {
|
||||
const app::Manifest& manifest;
|
||||
const app::AppManifest& manifest;
|
||||
} LoaderEventAppStopped;
|
||||
|
||||
typedef struct {
|
||||
@ -62,14 +63,14 @@ typedef enum {
|
||||
class LoaderMessageAppStart {
|
||||
public:
|
||||
std::string id;
|
||||
Bundle bundle;
|
||||
std::shared_ptr<const Bundle> _Nullable parameters;
|
||||
|
||||
LoaderMessageAppStart() = default;
|
||||
|
||||
LoaderMessageAppStart(const std::string& id, const Bundle& bundle) {
|
||||
this->id = id;
|
||||
this->bundle = bundle;
|
||||
}
|
||||
LoaderMessageAppStart(const std::string& id, std::shared_ptr<const Bundle> parameters) :
|
||||
id(id),
|
||||
parameters(std::move(parameters))
|
||||
{}
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -15,13 +15,13 @@ static const Configuration* config_instance = nullptr;
|
||||
// region Default services
|
||||
|
||||
namespace service {
|
||||
namespace gui { extern const Manifest manifest; }
|
||||
namespace loader { extern const Manifest manifest; }
|
||||
namespace screenshot { extern const Manifest manifest; }
|
||||
namespace statusbar { extern const Manifest manifest; }
|
||||
namespace gui { extern const ServiceManifest manifest; }
|
||||
namespace loader { extern const ServiceManifest manifest; }
|
||||
namespace screenshot { extern const ServiceManifest manifest; }
|
||||
namespace statusbar { extern const ServiceManifest manifest; }
|
||||
}
|
||||
|
||||
static const std::vector<const service::Manifest*> system_services = {
|
||||
static const std::vector<const service::ServiceManifest*> system_services = {
|
||||
&service::loader::manifest,
|
||||
&service::gui::manifest, // depends on loader service
|
||||
#ifndef ESP_PLATFORM // Screenshots don't work yet on ESP32
|
||||
@ -35,30 +35,30 @@ static const std::vector<const service::Manifest*> system_services = {
|
||||
// region Default apps
|
||||
|
||||
namespace app {
|
||||
namespace boot { extern const Manifest manifest; }
|
||||
namespace desktop { extern const Manifest manifest; }
|
||||
namespace files { extern const Manifest manifest; }
|
||||
namespace gpio { extern const Manifest manifest; }
|
||||
namespace imageviewer { extern const Manifest manifest; }
|
||||
namespace screenshot { extern const Manifest manifest; }
|
||||
namespace settings { extern const Manifest manifest; }
|
||||
namespace display { extern const Manifest manifest; }
|
||||
namespace i2cscanner { extern const Manifest manifest; }
|
||||
namespace i2csettings { extern const Manifest manifest; }
|
||||
namespace power { extern const Manifest manifest; }
|
||||
namespace selectiondialog { extern const Manifest manifest; }
|
||||
namespace systeminfo { extern const Manifest manifest; }
|
||||
namespace textviewer { extern const Manifest manifest; }
|
||||
namespace wifiapsettings { extern const Manifest manifest; }
|
||||
namespace wificonnect { extern const Manifest manifest; }
|
||||
namespace wifimanage { extern const Manifest manifest; }
|
||||
namespace boot { extern const AppManifest manifest; }
|
||||
namespace desktop { extern const AppManifest manifest; }
|
||||
namespace files { extern const AppManifest manifest; }
|
||||
namespace gpio { extern const AppManifest manifest; }
|
||||
namespace imageviewer { extern const AppManifest manifest; }
|
||||
namespace screenshot { extern const AppManifest manifest; }
|
||||
namespace settings { extern const AppManifest manifest; }
|
||||
namespace display { extern const AppManifest manifest; }
|
||||
namespace i2cscanner { extern const AppManifest manifest; }
|
||||
namespace i2csettings { extern const AppManifest manifest; }
|
||||
namespace power { extern const AppManifest manifest; }
|
||||
namespace selectiondialog { extern const AppManifest manifest; }
|
||||
namespace systeminfo { extern const AppManifest manifest; }
|
||||
namespace textviewer { extern const AppManifest manifest; }
|
||||
namespace wifiapsettings { extern const AppManifest manifest; }
|
||||
namespace wificonnect { extern const AppManifest manifest; }
|
||||
namespace wifimanage { extern const AppManifest manifest; }
|
||||
}
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
extern const app::Manifest screenshot_app;
|
||||
extern const app::AppManifest screenshot_app;
|
||||
#endif
|
||||
|
||||
static const std::vector<const app::Manifest*> system_apps = {
|
||||
static const std::vector<const app::AppManifest*> system_apps = {
|
||||
&app::boot::manifest,
|
||||
&app::desktop::manifest,
|
||||
&app::display::manifest,
|
||||
@ -92,10 +92,10 @@ static void register_system_apps() {
|
||||
}
|
||||
}
|
||||
|
||||
static void register_user_apps(const app::Manifest* const apps[TT_CONFIG_APPS_LIMIT]) {
|
||||
static void register_user_apps(const app::AppManifest* const apps[TT_CONFIG_APPS_LIMIT]) {
|
||||
TT_LOG_I(TAG, "Registering user apps");
|
||||
for (size_t i = 0; i < TT_CONFIG_APPS_LIMIT; i++) {
|
||||
const app::Manifest* manifest = apps[i];
|
||||
const app::AppManifest* manifest = apps[i];
|
||||
if (manifest != nullptr) {
|
||||
addApp(manifest);
|
||||
} else {
|
||||
@ -113,10 +113,10 @@ static void register_and_start_system_services() {
|
||||
}
|
||||
}
|
||||
|
||||
static void register_and_start_user_services(const service::Manifest* const services[TT_CONFIG_SERVICES_LIMIT]) {
|
||||
static void register_and_start_user_services(const service::ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]) {
|
||||
TT_LOG_I(TAG, "Registering and starting user services");
|
||||
for (size_t i = 0; i < TT_CONFIG_SERVICES_LIMIT; i++) {
|
||||
const service::Manifest* manifest = services[i];
|
||||
const service::ServiceManifest* manifest = services[i];
|
||||
if (manifest != nullptr) {
|
||||
addService(manifest);
|
||||
tt_check(service::startService(manifest->id));
|
||||
@ -152,11 +152,11 @@ void init(const Configuration& config) {
|
||||
register_user_apps(config.apps);
|
||||
|
||||
TT_LOG_I(TAG, "init starting desktop app");
|
||||
service::loader::startApp(app::boot::manifest.id, true, Bundle());
|
||||
service::loader::startApp(app::boot::manifest.id, true);
|
||||
|
||||
if (config.auto_start_app_id) {
|
||||
TT_LOG_I(TAG, "init auto-starting %s", config.auto_start_app_id);
|
||||
service::loader::startApp(config.auto_start_app_id, true, Bundle());
|
||||
service::loader::startApp(config.auto_start_app_id, true);
|
||||
}
|
||||
|
||||
TT_LOG_I(TAG, "init complete");
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/Manifest.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "hal/Configuration.h"
|
||||
#include "service/Manifest.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
#include "TactilityConfig.h"
|
||||
|
||||
namespace tt {
|
||||
@ -10,8 +10,8 @@ namespace tt {
|
||||
typedef struct {
|
||||
const hal::Configuration* hardware;
|
||||
// List of user applications
|
||||
const app::Manifest* const apps[TT_CONFIG_APPS_LIMIT];
|
||||
const service::Manifest* const services[TT_CONFIG_SERVICES_LIMIT];
|
||||
const app::AppManifest* const apps[TT_CONFIG_APPS_LIMIT];
|
||||
const service::ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT];
|
||||
const char* auto_start_app_id;
|
||||
} Configuration;
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Manifest.h"
|
||||
#include "Bundle.h"
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
bool showStatusbar : 1;
|
||||
};
|
||||
unsigned char flags;
|
||||
} Flags;
|
||||
|
||||
/**
|
||||
* A limited representation of the application instance.
|
||||
* Do not store references or pointers to these!
|
||||
*/
|
||||
class App {
|
||||
public:
|
||||
virtual ~App() {};
|
||||
virtual const Manifest& getManifest() const = 0;
|
||||
virtual _Nullable void* getData() const = 0;
|
||||
virtual void setData(void* data) = 0;
|
||||
virtual const Bundle& getParameters() const = 0;
|
||||
virtual Flags getFlags() const = 0;
|
||||
virtual void setResult(Result result, const Bundle& bundle = Bundle()) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
38
Tactility/Source/app/AppContext.h
Normal file
38
Tactility/Source/app/AppContext.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppManifest.h"
|
||||
#include "Bundle.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
bool showStatusbar : 1;
|
||||
};
|
||||
unsigned char flags;
|
||||
} Flags;
|
||||
|
||||
/**
|
||||
* A limited representation of the application instance.
|
||||
* Do not store references or pointers to these!
|
||||
*/
|
||||
class AppContext {
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~AppContext() = default;
|
||||
|
||||
public:
|
||||
|
||||
[[nodiscard]] virtual const AppManifest& getManifest() const = 0;
|
||||
[[nodiscard]] virtual std::shared_ptr<void> _Nullable getData() const = 0;
|
||||
virtual void setData(std::shared_ptr<void> data) = 0;
|
||||
[[nodiscard]] virtual std::shared_ptr<const Bundle> getParameters() const = 0;
|
||||
[[nodiscard]] virtual Flags getFlags() const = 0;
|
||||
virtual void setResult(Result result) = 0;
|
||||
virtual void setResult(Result result, std::shared_ptr<const Bundle> bundle)= 0;
|
||||
[[nodiscard]] virtual bool hasResult() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@ -23,7 +23,7 @@ State AppInstance::getState() const {
|
||||
* Consider creating MutableBundle vs Bundle.
|
||||
* Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it.
|
||||
*/
|
||||
const Manifest& AppInstance::getManifest() const {
|
||||
const AppManifest& AppInstance::getManifest() const {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@ -40,26 +40,45 @@ void AppInstance::setFlags(Flags newFlags) {
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
_Nullable void* AppInstance::getData() const {
|
||||
std::shared_ptr<void> _Nullable AppInstance::getData() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
auto result = data;
|
||||
mutex.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
void AppInstance::setData(void* newData) {
|
||||
void AppInstance::setData(std::shared_ptr<void> newData) {
|
||||
mutex.acquire(TtWaitForever);
|
||||
data = newData;
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
const Bundle& AppInstance::getParameters() const {
|
||||
return parameters;
|
||||
std::shared_ptr<const Bundle> AppInstance::getParameters() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
std::shared_ptr<const Bundle> result = parameters;
|
||||
mutex.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
void AppInstance::setResult(Result result, const Bundle& bundle) {
|
||||
std::unique_ptr<ResultHolder> new_holder(new ResultHolder(result, bundle));
|
||||
void AppInstance::setResult(Result result) {
|
||||
std::unique_ptr<ResultHolder> new_holder(new ResultHolder(result));
|
||||
mutex.acquire(TtWaitForever);
|
||||
resultHolder = std::move(new_holder);
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
void AppInstance::setResult(Result result, std::shared_ptr<const Bundle> bundle) {
|
||||
std::unique_ptr<ResultHolder> new_holder(new ResultHolder(result, std::move(bundle)));
|
||||
mutex.acquire(TtWaitForever);
|
||||
resultHolder = std::move(new_holder);
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
bool AppInstance::hasResult() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
bool has_result = resultHolder != nullptr;
|
||||
mutex.release();
|
||||
return has_result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -9,7 +9,7 @@ typedef struct _lv_obj_t lv_obj_t;
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
class App;
|
||||
class AppContext;
|
||||
|
||||
typedef enum {
|
||||
/** Boot screen, shown before desktop is launched. */
|
||||
@ -32,13 +32,13 @@ typedef enum {
|
||||
ResultError
|
||||
} Result;
|
||||
|
||||
typedef void (*AppOnStart)(App& app);
|
||||
typedef void (*AppOnStop)(App& app);
|
||||
typedef void (*AppOnShow)(App& app, lv_obj_t* parent);
|
||||
typedef void (*AppOnHide)(App& app);
|
||||
typedef void (*AppOnResult)(App& app, Result result, const Bundle& resultData);
|
||||
typedef void (*AppOnStart)(AppContext& app);
|
||||
typedef void (*AppOnStop)(AppContext& app);
|
||||
typedef void (*AppOnShow)(AppContext& app, lv_obj_t* parent);
|
||||
typedef void (*AppOnHide)(AppContext& app);
|
||||
typedef void (*AppOnResult)(AppContext& app, Result result, const Bundle& resultData);
|
||||
|
||||
struct Manifest {
|
||||
struct AppManifest {
|
||||
/**
|
||||
* The identifier by which the app is launched by the system and other apps.
|
||||
*/
|
||||
@ -86,7 +86,7 @@ struct Manifest {
|
||||
};
|
||||
|
||||
struct {
|
||||
bool operator()(const Manifest* left, const Manifest* right) const { return left->name < right->name; }
|
||||
bool operator()(const AppManifest* left, const AppManifest* right) const { return left->name < right->name; }
|
||||
} SortAppManifestByName;
|
||||
|
||||
} // namespace
|
||||
@ -7,12 +7,12 @@
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
typedef std::unordered_map<std::string, const Manifest*> AppManifestMap;
|
||||
typedef std::unordered_map<std::string, const AppManifest*> AppManifestMap;
|
||||
|
||||
static AppManifestMap app_manifest_map;
|
||||
static Mutex hash_mutex(MutexTypeNormal);
|
||||
|
||||
void addApp(const Manifest* manifest) {
|
||||
void addApp(const AppManifest* manifest) {
|
||||
TT_LOG_I(TAG, "Registering manifest %s", manifest->id.c_str());
|
||||
|
||||
hash_mutex.acquire(TtWaitForever);
|
||||
@ -20,16 +20,16 @@ void addApp(const Manifest* manifest) {
|
||||
hash_mutex.release();
|
||||
}
|
||||
|
||||
_Nullable const Manifest * findAppById(const std::string& id) {
|
||||
_Nullable const AppManifest * findAppById(const std::string& id) {
|
||||
hash_mutex.acquire(TtWaitForever);
|
||||
auto iterator = app_manifest_map.find(id);
|
||||
_Nullable const Manifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr;
|
||||
_Nullable const AppManifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr;
|
||||
hash_mutex.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const Manifest*> getApps() {
|
||||
std::vector<const Manifest*> manifests;
|
||||
std::vector<const AppManifest*> getApps() {
|
||||
std::vector<const AppManifest*> manifests;
|
||||
hash_mutex.acquire(TtWaitForever);
|
||||
for (const auto& item: app_manifest_map) {
|
||||
manifests.push_back(item.second);
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Manifest.h"
|
||||
#include "AppManifest.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
void addApp(const Manifest* manifest);
|
||||
const Manifest _Nullable* findAppById(const std::string& id);
|
||||
std::vector<const Manifest*> getApps();
|
||||
void addApp(const AppManifest* manifest);
|
||||
const AppManifest _Nullable* findAppById(const std::string& id);
|
||||
std::vector<const AppManifest*> getApps();
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <Thread.h>
|
||||
#include <Kernel.h>
|
||||
#include "Assets.h"
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "lvgl.h"
|
||||
#include "hal/Display.h"
|
||||
#include "service/loader/Loader.h"
|
||||
@ -48,8 +48,8 @@ static int32_t threadCallback(TT_UNUSED void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
|
||||
Data* data = (Data*)app.getData();
|
||||
static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
|
||||
lv_obj_t* image = lv_image_create(parent);
|
||||
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
|
||||
@ -59,19 +59,17 @@ static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
|
||||
data->thread.start();
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
Data* data = new Data();
|
||||
static void onStart(AppContext& app) {
|
||||
auto data = std::shared_ptr<Data>(new Data());
|
||||
app.setData(data);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
Data* data = (Data*)app.getData();
|
||||
static void onStop(AppContext& app) {
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
data->thread.join();
|
||||
tt_assert(data);
|
||||
delete data;
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Boot",
|
||||
.name = "Boot",
|
||||
.type = TypeBoot,
|
||||
|
||||
@ -10,12 +10,12 @@ namespace tt::app::desktop {
|
||||
static void onAppPressed(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::startApp(manifest->id, false, Bundle());
|
||||
const auto* manifest = static_cast<const AppManifest*>(lv_event_get_user_data(e));
|
||||
service::loader::startApp(manifest->id, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void createAppWidget(const Manifest* manifest, void* parent) {
|
||||
static void createAppWidget(const AppManifest* manifest, void* parent) {
|
||||
tt_check(parent);
|
||||
auto* list = static_cast<lv_obj_t*>(parent);
|
||||
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
|
||||
@ -23,7 +23,7 @@ static void createAppWidget(const Manifest* manifest, void* parent) {
|
||||
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
|
||||
}
|
||||
|
||||
static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
|
||||
static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_t* list = lv_list_create(parent);
|
||||
lv_obj_set_size(list, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_center(list);
|
||||
@ -46,7 +46,7 @@ static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Desktop",
|
||||
.name = "Desktop",
|
||||
.type = TypeDesktop,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "Assets.h"
|
||||
#include "DisplaySettings.h"
|
||||
#include "Tactility.h"
|
||||
@ -70,7 +70,7 @@ static void onOrientationSet(lv_event_t* event) {
|
||||
}
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
lvgl::toolbar_create(parent, app);
|
||||
@ -121,13 +121,13 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_dropdown_set_selected(orientation_dropdown, orientation_selected);
|
||||
}
|
||||
|
||||
static void onHide(TT_UNUSED App& app) {
|
||||
static void onHide(TT_UNUSED AppContext& app) {
|
||||
if (backlight_duty_set) {
|
||||
setBacklightDuty(backlight_duty);
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Display",
|
||||
.name = "Display",
|
||||
.icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "FilesData.h"
|
||||
#include "Tactility.h"
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "Assets.h"
|
||||
#include "Check.h"
|
||||
#include "FileUtils.h"
|
||||
@ -17,6 +17,18 @@ namespace tt::app::files {
|
||||
|
||||
#define TAG "files_app"
|
||||
|
||||
extern const AppManifest manifest;
|
||||
|
||||
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
|
||||
std::shared_ptr<Data> _Nullable optData() {
|
||||
app::AppContext* app = service::loader::getCurrentApp();
|
||||
if (app->getManifest().id == manifest.id) {
|
||||
return std::static_pointer_cast<Data>(app->getData());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Case-insensitive check to see if the given file matches the provided file extension.
|
||||
* @param path the full path to the file
|
||||
@ -57,10 +69,14 @@ static bool isSupportedTextFile(const char* filename) {
|
||||
|
||||
// region Views
|
||||
|
||||
static void updateViews(Data* data);
|
||||
static void updateViews(std::shared_ptr<Data> data);
|
||||
|
||||
static void onNavigateUpPressed(TT_UNUSED lv_event_t* event) {
|
||||
auto files_data = optData();
|
||||
if (files_data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void onNavigateUpPressed(lv_event_t* event) {
|
||||
auto* files_data = (Data*)lv_event_get_user_data(event);
|
||||
if (strcmp(files_data->current_path, "/") != 0) {
|
||||
TT_LOG_I(TAG, "Navigating upwards");
|
||||
char new_absolute_path[MAX_PATH_LENGTH];
|
||||
@ -68,6 +84,7 @@ static void onNavigateUpPressed(lv_event_t* event) {
|
||||
data_set_entries_for_path(files_data, new_absolute_path);
|
||||
}
|
||||
}
|
||||
|
||||
updateViews(files_data);
|
||||
}
|
||||
|
||||
@ -103,16 +120,16 @@ static void viewFile(const char* path, const char* filename) {
|
||||
TT_LOG_I(TAG, "Clicked %s", filepath);
|
||||
|
||||
if (isSupportedImageFile(filename)) {
|
||||
Bundle bundle;
|
||||
bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
|
||||
auto bundle = std::shared_ptr<Bundle>(new Bundle());
|
||||
bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
|
||||
service::loader::startApp("ImageViewer", false, bundle);
|
||||
} else if (isSupportedTextFile(filename)) {
|
||||
Bundle bundle;
|
||||
auto bundle = std::shared_ptr<Bundle>(new Bundle());
|
||||
if (get_platform() == PlatformEsp) {
|
||||
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath);
|
||||
bundle->putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath);
|
||||
} else {
|
||||
// Remove forward slash, because we need a relative path
|
||||
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1);
|
||||
bundle->putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1);
|
||||
}
|
||||
service::loader::startApp("TextViewer", false, bundle);
|
||||
} else {
|
||||
@ -123,11 +140,13 @@ static void viewFile(const char* path, const char* filename) {
|
||||
}
|
||||
|
||||
static void onFilePressed(lv_event_t* event) {
|
||||
auto files_data = optData();
|
||||
if (files_data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_event_code_t code = lv_event_get_code(event);
|
||||
if (code == LV_EVENT_CLICKED) {
|
||||
lv_obj_t* button = lv_event_get_current_target_obj(event);
|
||||
auto* files_data = static_cast<Data*>(lv_obj_get_user_data(button));
|
||||
|
||||
auto* dir_entry = static_cast<dirent*>(lv_event_get_user_data(event));
|
||||
TT_LOG_I(TAG, "Pressed %s %d", dir_entry->d_name, dir_entry->d_type);
|
||||
|
||||
@ -152,7 +171,7 @@ static void onFilePressed(lv_event_t* event) {
|
||||
}
|
||||
}
|
||||
|
||||
static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent* dir_entry) {
|
||||
static void createFileWidget(lv_obj_t* parent, struct dirent* dir_entry) {
|
||||
tt_check(parent);
|
||||
auto* list = (lv_obj_t*)parent;
|
||||
const char* symbol;
|
||||
@ -166,15 +185,14 @@ static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent*
|
||||
symbol = LV_SYMBOL_FILE;
|
||||
}
|
||||
lv_obj_t* button = lv_list_add_button(list, symbol, dir_entry->d_name);
|
||||
lv_obj_set_user_data(button, files_data);
|
||||
lv_obj_add_event_cb(button, &onFilePressed, LV_EVENT_CLICKED, (void*)dir_entry);
|
||||
}
|
||||
|
||||
static void updateViews(Data* data) {
|
||||
static void updateViews(std::shared_ptr<Data> data) {
|
||||
lv_obj_clean(data->list);
|
||||
for (int i = 0; i < data->dir_entries_count; ++i) {
|
||||
TT_LOG_I(TAG, "Entry: %s %d", data->dir_entries[i]->d_name, data->dir_entries[i]->d_type);
|
||||
createFileWidget(data, data->list, data->dir_entries[i]);
|
||||
createFileWidget(data->list, data->dir_entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,14 +200,14 @@ static void updateViews(Data* data) {
|
||||
|
||||
// region Lifecycle
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
auto* data = static_cast<Data*>(app.getData());
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
lv_obj_t* toolbar = lvgl::toolbar_create(parent, "Files");
|
||||
lvgl::toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &onExitAppPressed, nullptr);
|
||||
lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, data);
|
||||
lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, nullptr);
|
||||
|
||||
data->list = lv_list_create(parent);
|
||||
lv_obj_set_width(data->list, LV_PCT(100));
|
||||
@ -198,8 +216,8 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
updateViews(data);
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
auto* data = data_alloc();
|
||||
static void onStart(AppContext& app) {
|
||||
auto data = std::shared_ptr<Data>(new Data());
|
||||
// PC platform is bound to current work directory because of the LVGL file system mapping
|
||||
if (get_platform() == PlatformSimulator) {
|
||||
char cwd[PATH_MAX];
|
||||
@ -216,20 +234,14 @@ static void onStart(App& app) {
|
||||
app.setData(data);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
auto* data = static_cast<Data*>(app.getData());
|
||||
data_free(data);
|
||||
}
|
||||
|
||||
// endregion Lifecycle
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Files",
|
||||
.name = "Files",
|
||||
.icon = TT_ASSETS_APP_ICON_FILES,
|
||||
.type = TypeSystem,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
};
|
||||
|
||||
|
||||
@ -26,40 +26,16 @@ static bool get_child_path(char* base_path, const char* child_path, char* out_pa
|
||||
}
|
||||
}
|
||||
|
||||
Data* data_alloc() {
|
||||
auto* data = static_cast<Data*>(malloc(sizeof(Data)));
|
||||
*data = (Data) {
|
||||
.current_path = { 0x00 },
|
||||
.dir_entries = nullptr,
|
||||
.dir_entries_count = 0
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
void data_free(Data* data) {
|
||||
data_free_entries(data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void data_free_entries(Data* data) {
|
||||
for (int i = 0; i < data->dir_entries_count; ++i) {
|
||||
free(data->dir_entries[i]);
|
||||
}
|
||||
free(data->dir_entries);
|
||||
data->dir_entries = nullptr;
|
||||
data->dir_entries_count = 0;
|
||||
}
|
||||
|
||||
static void data_set_entries(Data* data, struct dirent** entries, int count) {
|
||||
static void data_set_entries(std::shared_ptr<Data> data, struct dirent** entries, int count) {
|
||||
if (data->dir_entries != nullptr) {
|
||||
data_free_entries(data);
|
||||
data->freeEntries();
|
||||
}
|
||||
|
||||
data->dir_entries = entries;
|
||||
data->dir_entries_count = count;
|
||||
}
|
||||
|
||||
bool data_set_entries_for_path(Data* data, const char* path) {
|
||||
bool data_set_entries_for_path(std::shared_ptr<Data> data, const char* path) {
|
||||
TT_LOG_I(TAG, "Changing path: %s -> %s", data->current_path, path);
|
||||
|
||||
/**
|
||||
@ -100,7 +76,7 @@ bool data_set_entries_for_path(Data* data, const char* path) {
|
||||
}
|
||||
}
|
||||
|
||||
bool data_set_entries_for_child_path(Data* data, const char* child_path) {
|
||||
bool data_set_entries_for_child_path(std::shared_ptr<Data> data, const char* child_path) {
|
||||
char new_absolute_path[MAX_PATH_LENGTH + 1];
|
||||
if (get_child_path(data->current_path, child_path, new_absolute_path, MAX_PATH_LENGTH)) {
|
||||
TT_LOG_I(TAG, "Navigating from %s to %s", data->current_path, new_absolute_path);
|
||||
|
||||
@ -1,23 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include "lvgl.h"
|
||||
#include <dirent.h>
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app::files {
|
||||
|
||||
#define MAX_PATH_LENGTH 256
|
||||
|
||||
typedef struct {
|
||||
char current_path[MAX_PATH_LENGTH];
|
||||
struct dirent** dir_entries;
|
||||
int dir_entries_count;
|
||||
lv_obj_t* list;
|
||||
} Data;
|
||||
struct Data {
|
||||
char current_path[MAX_PATH_LENGTH] = { 0 };
|
||||
struct dirent** dir_entries = nullptr;
|
||||
int dir_entries_count = 0;
|
||||
lv_obj_t* list = nullptr;
|
||||
|
||||
Data* data_alloc();
|
||||
void data_free(Data* data);
|
||||
void data_free_entries(Data* data);
|
||||
bool data_set_entries_for_child_path(Data* data, const char* child_path);
|
||||
bool data_set_entries_for_path(Data* data, const char* path);
|
||||
void freeEntries() {
|
||||
for (int i = 0; i < dir_entries_count; ++i) {
|
||||
free(dir_entries[i]);
|
||||
}
|
||||
free(dir_entries);
|
||||
dir_entries = nullptr;
|
||||
dir_entries_count = 0;
|
||||
}
|
||||
|
||||
~Data() {
|
||||
freeEntries();
|
||||
}
|
||||
};
|
||||
|
||||
void data_free(std::shared_ptr<Data> data);
|
||||
void data_free_entries(std::shared_ptr<Data> data);
|
||||
bool data_set_entries_for_child_path(std::shared_ptr<Data> data, const char* child_path);
|
||||
bool data_set_entries_for_path(std::shared_ptr<Data> data, const char* path);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -29,8 +29,8 @@ public:
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
}
|
||||
|
||||
void onShow(App& app, lv_obj_t* parent);
|
||||
void onHide(App& app);
|
||||
void onShow(AppContext& app, lv_obj_t* parent);
|
||||
void onHide(AppContext& app);
|
||||
|
||||
void startTask();
|
||||
void stopTask();
|
||||
@ -135,8 +135,8 @@ void Gpio::stopTask() {
|
||||
// endregion Task
|
||||
|
||||
|
||||
void Gpio::onShow(App& app, lv_obj_t* parent) {
|
||||
auto* gpio = (Gpio*)app.getData();
|
||||
void Gpio::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);
|
||||
@ -191,42 +191,36 @@ void Gpio::onShow(App& app, lv_obj_t* parent) {
|
||||
gpio->startTask();
|
||||
}
|
||||
|
||||
void Gpio::onHide(App& app) {
|
||||
auto* gpio = (Gpio*)app.getData();
|
||||
void Gpio::onHide(AppContext& app) {
|
||||
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
|
||||
gpio->stopTask();
|
||||
}
|
||||
|
||||
|
||||
// region App lifecycle
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
auto* gpio = (Gpio*)app.getData();
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
|
||||
gpio->onShow(app, parent);
|
||||
}
|
||||
|
||||
static void onHide(App& app) {
|
||||
auto* gpio = (Gpio*)app.getData();
|
||||
static void onHide(AppContext& app) {
|
||||
auto gpio = std::static_pointer_cast<Gpio>(app.getData());
|
||||
gpio->onHide(app);
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
auto* gpio = new Gpio();
|
||||
static void onStart(AppContext& app) {
|
||||
auto gpio = std::shared_ptr<Gpio>(new Gpio());
|
||||
app.setData(gpio);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
auto* gpio = (Gpio*)app.getData();
|
||||
delete gpio;
|
||||
}
|
||||
|
||||
// endregion App lifecycle
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Gpio",
|
||||
.name = "GPIO",
|
||||
.type = TypeSystem,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
.onHide = onHide
|
||||
};
|
||||
|
||||
@ -14,7 +14,6 @@ std::string getAddressText(uint8_t address) {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
|
||||
std::string getPortNamesForDropdown() {
|
||||
std::vector<std::string> config_names;
|
||||
size_t port_index = 0;
|
||||
|
||||
@ -4,21 +4,37 @@
|
||||
|
||||
#include "Assets.h"
|
||||
#include "Tactility.h"
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "lvgl/LvglSync.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
#include "service/loader/Loader.h"
|
||||
|
||||
#define START_SCAN_TEXT "Scan"
|
||||
#define STOP_SCAN_TEXT "Stop scan"
|
||||
|
||||
namespace tt::app::i2cscanner {
|
||||
|
||||
static void updateViews(Data* data);
|
||||
static void updateViews(std::shared_ptr<Data> data);
|
||||
extern const AppManifest manifest;
|
||||
|
||||
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
|
||||
std::shared_ptr<Data> _Nullable optData() {
|
||||
app::AppContext* app = service::loader::getCurrentApp();
|
||||
if (app->getManifest().id == manifest.id) {
|
||||
return std::static_pointer_cast<Data>(app->getData());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void onSelectBus(lv_event_t* event) {
|
||||
auto data = optData();
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
||||
uint32_t selected = lv_dropdown_get_selected(dropdown);
|
||||
Data* data = (Data*) lv_event_get_user_data(event);
|
||||
auto i2c_devices = tt::getConfiguration()->hardware->i2c;
|
||||
assert(selected < i2c_devices.size());
|
||||
|
||||
@ -35,16 +51,18 @@ static void onSelectBus(lv_event_t* event) {
|
||||
}
|
||||
|
||||
static void onPressScan(lv_event_t* event) {
|
||||
auto* data = (Data*)lv_event_get_user_data(event);
|
||||
if (data->scanState == ScanStateScanning) {
|
||||
stopScanning(data);
|
||||
} else {
|
||||
startScanning(data);
|
||||
auto data = optData();
|
||||
if (data != nullptr) {
|
||||
if (data->scanState == ScanStateScanning) {
|
||||
stopScanning(data);
|
||||
} else {
|
||||
startScanning(data);
|
||||
}
|
||||
updateViews(data);
|
||||
}
|
||||
updateViews(data);
|
||||
}
|
||||
|
||||
static void updateViews(Data* data) {
|
||||
static void updateViews(std::shared_ptr<Data> data) {
|
||||
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (data->scanState == ScanStateScanning) {
|
||||
lv_label_set_text(data->scanButtonLabelWidget, STOP_SCAN_TEXT);
|
||||
@ -75,7 +93,7 @@ static void updateViews(Data* data) {
|
||||
}
|
||||
}
|
||||
|
||||
static void updateViewsSafely(Data* data) {
|
||||
static void updateViewsSafely(std::shared_ptr<Data> data) {
|
||||
if (lvgl::lock(100 / portTICK_PERIOD_MS)) {
|
||||
updateViews(data);
|
||||
lvgl::unlock();
|
||||
@ -84,7 +102,7 @@ static void updateViewsSafely(Data* data) {
|
||||
}
|
||||
}
|
||||
|
||||
void onThreadFinished(Data* data) {
|
||||
void onThreadFinished(std::shared_ptr<Data> data) {
|
||||
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (data->scanState == ScanStateScanning) {
|
||||
data->scanState = ScanStateStopped;
|
||||
@ -96,8 +114,8 @@ void onThreadFinished(Data* data) {
|
||||
}
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
auto* data = (Data*)app.getData();
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
lvgl::toolbar_create(parent, app);
|
||||
@ -116,7 +134,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_obj_t* scan_button = lv_button_create(wrapper);
|
||||
lv_obj_set_width(scan_button, LV_PCT(48));
|
||||
lv_obj_align(scan_button, LV_ALIGN_TOP_LEFT, 0, 1); // Shift 1 pixel to align with selection box
|
||||
lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, data);
|
||||
lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, nullptr);
|
||||
lv_obj_t* scan_button_label = lv_label_create(scan_button);
|
||||
lv_obj_align(scan_button_label, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_label_set_text(scan_button_label, START_SCAN_TEXT);
|
||||
@ -127,7 +145,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_dropdown_set_options(port_dropdown, dropdown_items.c_str());
|
||||
lv_obj_set_width(port_dropdown, LV_PCT(48));
|
||||
lv_obj_align(port_dropdown, LV_ALIGN_TOP_RIGHT, 0, 0);
|
||||
lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, data);
|
||||
lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
lv_dropdown_set_selected(port_dropdown, 0);
|
||||
data->portDropdownWidget = port_dropdown;
|
||||
|
||||
@ -139,30 +157,24 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
data->scanListWidget = scan_list;
|
||||
}
|
||||
|
||||
static void onHide(App& app) {
|
||||
auto* data = (Data*)app.getData();
|
||||
static void onHide(AppContext& app) {
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
if (hasScanThread(data)) {
|
||||
stopScanning(data);
|
||||
}
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
Data* data = new Data();
|
||||
static void onStart(AppContext& app) {
|
||||
auto data = std::shared_ptr<Data>(new Data());
|
||||
app.setData(data);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
Data* data = (Data*)app.getData();
|
||||
delete data;
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "I2cScanner",
|
||||
.name = "I2C Scanner",
|
||||
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
|
||||
.type = TypeSystem,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
.onHide = onHide
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <Thread.h>
|
||||
#include "lvgl.h"
|
||||
#include "hal/i2c/I2c.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app::i2cscanner {
|
||||
|
||||
@ -30,6 +31,6 @@ struct Data {
|
||||
lv_obj_t* scanListWidget = nullptr;
|
||||
};
|
||||
|
||||
void onThreadFinished(Data* data);
|
||||
void onThreadFinished(std::shared_ptr<Data> data);
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
#include "I2cScannerThread.h"
|
||||
#include "lvgl.h"
|
||||
#include "service/gui/Gui.h"
|
||||
#include <iomanip>
|
||||
#include "service/loader/Loader.h"
|
||||
|
||||
namespace tt::app::i2cscanner {
|
||||
|
||||
static bool shouldStopScanThread(Data* data) {
|
||||
std::shared_ptr<Data> _Nullable optData();
|
||||
|
||||
static bool shouldStopScanThread(std::shared_ptr<Data> data) {
|
||||
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
bool is_scanning = data->scanState == ScanStateScanning;
|
||||
tt_check(data->mutex.release() == TtStatusOk);
|
||||
@ -15,7 +16,7 @@ static bool shouldStopScanThread(Data* data) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool getPort(Data* data, i2c_port_t* port) {
|
||||
static bool getPort(std::shared_ptr<Data> data, i2c_port_t* port) {
|
||||
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
*port = data->port;
|
||||
tt_assert(data->mutex.release() == TtStatusOk);
|
||||
@ -26,7 +27,7 @@ static bool getPort(Data* data, i2c_port_t* port) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool addAddressToList(Data* data, uint8_t address) {
|
||||
static bool addAddressToList(std::shared_ptr<Data> data, uint8_t address) {
|
||||
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
data->scannedAddresses.push_back(address);
|
||||
tt_assert(data->mutex.release() == TtStatusOk);
|
||||
@ -37,8 +38,12 @@ static bool addAddressToList(Data* data, uint8_t address) {
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t scanThread(void* context) {
|
||||
Data* data = (Data*) context;
|
||||
static int32_t scanThread(TT_UNUSED void* context) {
|
||||
auto data = optData();
|
||||
if (data == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
TT_LOG_I(TAG, "Scan thread started");
|
||||
|
||||
for (uint8_t address = 0; address < 128; ++address) {
|
||||
@ -69,7 +74,7 @@ static int32_t scanThread(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hasScanThread(Data* data) {
|
||||
bool hasScanThread(std::shared_ptr<Data> data) {
|
||||
bool has_thread;
|
||||
if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
has_thread = data->scanThread != nullptr;
|
||||
@ -82,7 +87,7 @@ bool hasScanThread(Data* data) {
|
||||
}
|
||||
}
|
||||
|
||||
void startScanning(Data* data) {
|
||||
void startScanning(std::shared_ptr<Data> data) {
|
||||
if (hasScanThread(data)) {
|
||||
stopScanning(data);
|
||||
}
|
||||
@ -99,7 +104,7 @@ void startScanning(Data* data) {
|
||||
"i2c scanner",
|
||||
4096,
|
||||
scanThread,
|
||||
data
|
||||
nullptr
|
||||
);
|
||||
data->scanThread->start();
|
||||
tt_check(data->mutex.release() == TtStatusOk);
|
||||
@ -108,7 +113,7 @@ void startScanning(Data* data) {
|
||||
}
|
||||
}
|
||||
|
||||
void stopScanning(Data* data) {
|
||||
void stopScanning(std::shared_ptr<Data> data) {
|
||||
bool sent_halt;
|
||||
if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
tt_assert(data->scanThread != nullptr);
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "I2cScanner.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app::i2cscanner {
|
||||
|
||||
bool hasScanThread(Data* data);
|
||||
void startScanning(Data* data);
|
||||
void stopScanning(Data* data);
|
||||
bool hasScanThread(std::shared_ptr<Data> data);
|
||||
void startScanning(std::shared_ptr<Data> data);
|
||||
void stopScanning(std::shared_ptr<Data> data);
|
||||
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ static void show(lv_obj_t* parent, const hal::i2c::Configuration& configuration)
|
||||
}
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::toolbar_create(parent, app);
|
||||
|
||||
@ -85,7 +85,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "I2cSettings",
|
||||
.name = "I2C",
|
||||
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include <TactilityCore.h>
|
||||
#include "ImageViewer.h"
|
||||
#include "Log.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
@ -8,7 +8,7 @@ namespace tt::app::imageviewer {
|
||||
|
||||
#define TAG "image_viewer"
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::toolbar_create(parent, app);
|
||||
|
||||
@ -22,16 +22,17 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_obj_t* image = lv_img_create(wrapper);
|
||||
lv_obj_align(image, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
const Bundle& bundle = app.getParameters();
|
||||
std::shared_ptr<const Bundle> bundle = app.getParameters();
|
||||
tt_check(bundle != nullptr, "Parameters not set");
|
||||
std::string file_argument;
|
||||
if (bundle.optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) {
|
||||
if (bundle->optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) {
|
||||
std::string prefixed_path = "A:" + file_argument;
|
||||
TT_LOG_I(TAG, "Opening %s", prefixed_path.c_str());
|
||||
lv_img_set_src(image, prefixed_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "ImageViewer",
|
||||
.name = "Image Viewer",
|
||||
.type = TypeHidden,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "Assets.h"
|
||||
#include "lvgl.h"
|
||||
#include "Tactility.h"
|
||||
@ -6,22 +6,35 @@
|
||||
#include "lvgl/LvglSync.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
#include "service/loader/Loader.h"
|
||||
|
||||
namespace tt::app::power {
|
||||
|
||||
#define TAG "power"
|
||||
|
||||
typedef struct {
|
||||
Timer* update_timer;
|
||||
extern const AppManifest manifest;
|
||||
static void on_timer(TT_UNUSED void* context);
|
||||
|
||||
struct Data {
|
||||
std::unique_ptr<Timer> update_timer = std::unique_ptr<Timer>(new Timer(Timer::TypePeriodic, &on_timer, nullptr));
|
||||
const hal::Power* power;
|
||||
lv_obj_t* enable_switch;
|
||||
lv_obj_t* charge_state;
|
||||
lv_obj_t* charge_level;
|
||||
lv_obj_t* current;
|
||||
} Data;
|
||||
};
|
||||
|
||||
static void updateUi(void* callbackContext) {
|
||||
auto* data = (Data*)callbackContext;
|
||||
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
|
||||
std::shared_ptr<Data> _Nullable optData() {
|
||||
app::AppContext* app = service::loader::getCurrentApp();
|
||||
if (app->getManifest().id == manifest.id) {
|
||||
return std::static_pointer_cast<Data>(app->getData());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void updateUi(std::shared_ptr<Data> data) {
|
||||
bool charging_enabled = data->power->isChargingEnabled();
|
||||
const char* charge_state = data->power->isCharging() ? "yes" : "no";
|
||||
uint8_t charge_level = data->power->getChargeLevel();
|
||||
@ -40,20 +53,30 @@ static void updateUi(void* callbackContext) {
|
||||
lvgl::unlock();
|
||||
}
|
||||
|
||||
static void on_timer(TT_UNUSED void* context) {
|
||||
auto data = optData();
|
||||
if (data != nullptr) {
|
||||
updateUi(data);
|
||||
}
|
||||
}
|
||||
|
||||
static void onPowerEnabledChanged(lv_event_t* event) {
|
||||
lv_event_code_t code = lv_event_get_code(event);
|
||||
auto* enable_switch = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
||||
if (code == LV_EVENT_VALUE_CHANGED) {
|
||||
bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED);
|
||||
auto* data = static_cast<Data*>(lv_event_get_user_data(event));
|
||||
if (data->power->isChargingEnabled() != is_on) {
|
||||
data->power->setChargingEnabled(is_on);
|
||||
updateUi(data);
|
||||
|
||||
auto data = optData();
|
||||
if (data != nullptr) {
|
||||
if (data->power->isChargingEnabled() != is_on) {
|
||||
data->power->setChargingEnabled(is_on);
|
||||
updateUi(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
lvgl::toolbar_create(parent, app);
|
||||
@ -64,7 +87,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_grow(wrapper, 1);
|
||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
auto* data = static_cast<Data*>(app.getData());
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
|
||||
// Top row: enable/disable
|
||||
lv_obj_t* switch_container = lv_obj_create(wrapper);
|
||||
@ -78,7 +101,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID);
|
||||
|
||||
lv_obj_t* enable_switch = lv_switch_create(switch_container);
|
||||
lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, data);
|
||||
lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID);
|
||||
|
||||
data->enable_switch = enable_switch;
|
||||
@ -90,32 +113,24 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
data->update_timer->start(ms_to_ticks(1000));
|
||||
}
|
||||
|
||||
static void onHide(TT_UNUSED App& app) {
|
||||
auto* data = static_cast<Data*>(app.getData());
|
||||
static void onHide(TT_UNUSED AppContext& app) {
|
||||
auto data = std::static_pointer_cast<Data>(app.getData());
|
||||
data->update_timer->stop();
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
auto* data = new Data();
|
||||
data->update_timer = new Timer(Timer::TypePeriodic, &updateUi, data);
|
||||
static void onStart(AppContext& app) {
|
||||
auto data = std::shared_ptr<Data>(new Data());
|
||||
app.setData(data);
|
||||
data->power = getConfiguration()->hardware->power;
|
||||
assert(data->power != nullptr); // The Power app only shows up on supported devices
|
||||
app.setData(data);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
auto* data = static_cast<Data*>(app.getData());
|
||||
delete data->update_timer;
|
||||
delete data;
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Power",
|
||||
.name = "Power",
|
||||
.icon = TT_ASSETS_APP_ICON_POWER_SETTINGS,
|
||||
.type = TypeSettings,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
.onHide = onHide
|
||||
};
|
||||
|
||||
@ -1,29 +1,24 @@
|
||||
#include "ScreenshotUi.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app::screenshot {
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
auto* ui = static_cast<ScreenshotUi*>(app.getData());
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto ui = std::static_pointer_cast<ScreenshotUi>(app.getData());
|
||||
create_ui(app, ui, parent);
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
auto* ui = static_cast<ScreenshotUi*>(malloc(sizeof(ScreenshotUi)));
|
||||
app.setData(ui);
|
||||
static void onStart(AppContext& app) {
|
||||
auto ui = std::shared_ptr<ScreenshotUi>(new ScreenshotUi());
|
||||
app.setData(ui); // Ensure data gets deleted when no more in use
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
auto* ui = static_cast<ScreenshotUi*>(app.getData());
|
||||
free(ui);
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Screenshot",
|
||||
.name = "_Screenshot", // So it gets put at the bottom of the desktop and becomes less visible on small screen devices
|
||||
.icon = LV_SYMBOL_IMAGE,
|
||||
.type = TypeSystem,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
};
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "TactilityCore.h"
|
||||
#include "hal/sdcard/Sdcard.h"
|
||||
#include "service/gui/Gui.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include "service/screenshot/Screenshot.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
|
||||
@ -10,29 +11,24 @@ namespace tt::app::screenshot {
|
||||
|
||||
#define TAG "screenshot_ui"
|
||||
|
||||
static void update_mode(ScreenshotUi* ui) {
|
||||
lv_obj_t* label = ui->start_stop_button_label;
|
||||
if (service::screenshot::isStarted()) {
|
||||
lv_label_set_text(label, "Stop");
|
||||
} else {
|
||||
lv_label_set_text(label, "Start");
|
||||
}
|
||||
extern AppManifest manifest;
|
||||
static void update_mode(std::shared_ptr<ScreenshotUi> ui);
|
||||
|
||||
uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown);
|
||||
if (selected == 0) { // Timer
|
||||
lv_obj_remove_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
|
||||
std::shared_ptr<ScreenshotUi> _Nullable optScreenshotUi() {
|
||||
app::AppContext* app = service::loader::getCurrentApp();
|
||||
if (app->getManifest().id == manifest.id) {
|
||||
return std::static_pointer_cast<ScreenshotUi>(app->getData());
|
||||
} else {
|
||||
lv_obj_add_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_mode_set(lv_event_t* event) {
|
||||
auto* ui = (ScreenshotUi*)lv_event_get_user_data(event);
|
||||
update_mode(ui);
|
||||
}
|
||||
|
||||
static void on_start_pressed(lv_event_t* event) {
|
||||
auto* ui = static_cast<ScreenshotUi*>(lv_event_get_user_data(event));
|
||||
auto ui = optScreenshotUi();
|
||||
if (ui == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (service::screenshot::isStarted()) {
|
||||
TT_LOG_I(TAG, "Stop screenshot");
|
||||
@ -58,7 +54,30 @@ static void on_start_pressed(lv_event_t* event) {
|
||||
update_mode(ui);
|
||||
}
|
||||
|
||||
static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
static void update_mode(std::shared_ptr<ScreenshotUi> ui) {
|
||||
lv_obj_t* label = ui->start_stop_button_label;
|
||||
if (service::screenshot::isStarted()) {
|
||||
lv_label_set_text(label, "Stop");
|
||||
} else {
|
||||
lv_label_set_text(label, "Start");
|
||||
}
|
||||
|
||||
uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown);
|
||||
if (selected == 0) { // Timer
|
||||
lv_obj_remove_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_obj_add_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_mode_set(lv_event_t* event) {
|
||||
auto ui = optScreenshotUi();
|
||||
if (ui != nullptr) {
|
||||
update_mode(ui);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_mode_setting_ui(std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
|
||||
lv_obj_t* mode_wrapper = lv_obj_create(parent);
|
||||
lv_obj_set_size(mode_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(mode_wrapper, 0, 0);
|
||||
@ -71,7 +90,7 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
lv_obj_t* mode_dropdown = lv_dropdown_create(mode_wrapper);
|
||||
lv_dropdown_set_options(mode_dropdown, "Timer\nApp start");
|
||||
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);
|
||||
lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
ui->mode_dropdown = mode_dropdown;
|
||||
service::screenshot::Mode mode = service::screenshot::getMode();
|
||||
if (mode == service::screenshot::ScreenshotModeApps) {
|
||||
@ -83,10 +102,10 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
lv_obj_t* button_label = lv_label_create(button);
|
||||
lv_obj_align(button_label, LV_ALIGN_CENTER, 0, 0);
|
||||
ui->start_stop_button_label = button_label;
|
||||
lv_obj_add_event_cb(button, &on_start_pressed, LV_EVENT_CLICKED, ui);
|
||||
lv_obj_add_event_cb(button, &on_start_pressed, LV_EVENT_CLICKED, nullptr);
|
||||
}
|
||||
|
||||
static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
static void create_path_ui(std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
|
||||
lv_obj_t* path_wrapper = lv_obj_create(parent);
|
||||
lv_obj_set_size(path_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(path_wrapper, 0, 0);
|
||||
@ -116,7 +135,7 @@ static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
static void create_timer_settings_ui(std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
|
||||
lv_obj_t* timer_wrapper = lv_obj_create(parent);
|
||||
lv_obj_set_size(timer_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(timer_wrapper, 0, 0);
|
||||
@ -153,7 +172,7 @@ static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
lv_label_set_text(delay_unit_label, "seconds");
|
||||
}
|
||||
|
||||
void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent) {
|
||||
void create_ui(const AppContext& app, std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::app::screenshot {
|
||||
@ -13,6 +13,6 @@ typedef struct {
|
||||
lv_obj_t* delay_textarea;
|
||||
} ScreenshotUi;
|
||||
|
||||
void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent);
|
||||
void create_ui(const AppContext& app, std::shared_ptr<ScreenshotUi> ui, lv_obj_t* parent);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#include "SelectionDialog.h"
|
||||
#include "Log.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include <StringUtils.h>
|
||||
#include <TactilityCore.h>
|
||||
|
||||
namespace tt::app::selectiondialog {
|
||||
|
||||
@ -27,17 +27,17 @@ int32_t getResultIndex(const Bundle& bundle) {
|
||||
return index;
|
||||
}
|
||||
|
||||
void setResultIndex(Bundle& bundle, int32_t index) {
|
||||
bundle.putInt32(RESULT_BUNDLE_KEY_INDEX, index);
|
||||
void setResultIndex(std::shared_ptr<Bundle> bundle, int32_t index) {
|
||||
bundle->putInt32(RESULT_BUNDLE_KEY_INDEX, index);
|
||||
}
|
||||
|
||||
void setTitleParameter(Bundle& bundle, const std::string& title) {
|
||||
bundle.putString(PARAMETER_BUNDLE_KEY_TITLE, title);
|
||||
void setTitleParameter(std::shared_ptr<Bundle> bundle, const std::string& title) {
|
||||
bundle->putString(PARAMETER_BUNDLE_KEY_TITLE, title);
|
||||
}
|
||||
|
||||
static std::string getTitleParameter(const Bundle& bundle) {
|
||||
static std::string getTitleParameter(std::shared_ptr<const Bundle> bundle) {
|
||||
std::string result;
|
||||
if (bundle.optString(PARAMETER_BUNDLE_KEY_TITLE, result)) {
|
||||
if (bundle->optString(PARAMETER_BUNDLE_KEY_TITLE, result)) {
|
||||
return result;
|
||||
} else {
|
||||
return DEFAULT_TITLE;
|
||||
@ -49,8 +49,8 @@ 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::getCurrentApp();
|
||||
Bundle bundle;
|
||||
tt::app::AppContext* app = service::loader::getCurrentApp();
|
||||
auto bundle = std::shared_ptr<Bundle>(new Bundle());
|
||||
setResultIndex(bundle, (int32_t)index);
|
||||
app->setResult(app::ResultOk, bundle);
|
||||
service::loader::stopApp();
|
||||
@ -63,7 +63,7 @@ static void createChoiceItem(void* parent, const std::string& title, size_t inde
|
||||
lv_obj_add_event_cb(btn, &onListItemSelected, LV_EVENT_CLICKED, (void*)index);
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
std::string title = getTitleParameter(app.getParameters());
|
||||
lvgl::toolbar_create(parent, title);
|
||||
@ -72,16 +72,17 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_obj_set_width(list, LV_PCT(100));
|
||||
lv_obj_set_flex_grow(list, 1);
|
||||
|
||||
const Bundle& parameters = app.getParameters();
|
||||
auto parameters = app.getParameters();
|
||||
tt_check(parameters != nullptr, "No parameters");
|
||||
std::string items_concatenated;
|
||||
if (parameters.optString(PARAMETER_BUNDLE_KEY_ITEMS, items_concatenated)) {
|
||||
if (parameters->optString(PARAMETER_BUNDLE_KEY_ITEMS, items_concatenated)) {
|
||||
std::vector<std::string> items = string_split(items_concatenated, PARAMETER_ITEM_CONCATENATION_TOKEN);
|
||||
if (items.empty() || items.front().empty()) {
|
||||
TT_LOG_E(TAG, "No items provided");
|
||||
app.setResult(ResultError);
|
||||
service::loader::stopApp();
|
||||
} else if (items.size() == 1) {
|
||||
Bundle result_bundle;
|
||||
auto result_bundle = std::shared_ptr<Bundle>(new Bundle());
|
||||
setResultIndex(result_bundle, 0);
|
||||
app.setResult(ResultOk, result_bundle);
|
||||
service::loader::stopApp();
|
||||
@ -99,7 +100,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "SelectionDialog",
|
||||
.name = "Selection Dialog",
|
||||
.type = TypeHidden,
|
||||
|
||||
@ -11,12 +11,12 @@ namespace tt::app::settings {
|
||||
static void onAppPressed(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));
|
||||
const auto* manifest = static_cast<const AppManifest*>(lv_event_get_user_data(e));
|
||||
service::loader::startApp(manifest->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void createWidget(const Manifest* manifest, void* parent) {
|
||||
static void createWidget(const AppManifest* manifest, void* parent) {
|
||||
tt_check(parent);
|
||||
auto* list = (lv_obj_t*)parent;
|
||||
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
|
||||
@ -24,7 +24,7 @@ static void createWidget(const Manifest* manifest, void* parent) {
|
||||
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
lvgl::toolbar_create(parent, app);
|
||||
@ -42,7 +42,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "Settings",
|
||||
.name = "Settings",
|
||||
.icon = TT_ASSETS_APP_ICON_SETTINGS,
|
||||
|
||||
@ -108,7 +108,7 @@ static void addRtosTasks(lv_obj_t* parent) {
|
||||
|
||||
#endif
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::toolbar_create(parent, app);
|
||||
|
||||
@ -151,7 +151,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
#endif
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "SystemInfo",
|
||||
.name = "System Info",
|
||||
.icon = TT_ASSETS_APP_ICON_SYSTEM_INFO,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "Log.h"
|
||||
#include <TactilityCore.h>
|
||||
#include "TextViewer.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/LabelUtils.h"
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
namespace tt::app::textviewer {
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::toolbar_create(parent, app);
|
||||
|
||||
@ -22,9 +22,10 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
|
||||
lv_obj_t* label = lv_label_create(wrapper);
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
const Bundle& bundle = app.getParameters();
|
||||
auto parameters = app.getParameters();
|
||||
tt_check(parameters != nullptr, "No parameters");
|
||||
std::string file_argument;
|
||||
if (bundle.optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) {
|
||||
if (parameters->optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) {
|
||||
TT_LOG_I(TAG, "Opening %s", file_argument.c_str());
|
||||
lvgl::label_set_text_file(label, file_argument.c_str());
|
||||
} else {
|
||||
@ -32,7 +33,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "TextViewer",
|
||||
.name = "Text Viewer",
|
||||
.type = TypeHidden,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "WifiApSettings.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
@ -11,11 +11,11 @@ namespace tt::app::wifiapsettings {
|
||||
|
||||
#define TAG "wifi_ap_settings"
|
||||
|
||||
extern const Manifest manifest;
|
||||
extern const AppManifest manifest;
|
||||
|
||||
void start(const std::string& ssid) {
|
||||
Bundle bundle;
|
||||
bundle.putString("ssid", ssid);
|
||||
auto bundle = std::shared_ptr<Bundle>(new Bundle());
|
||||
bundle->putString("ssid", ssid);
|
||||
service::loader::startApp(manifest.id, false, bundle);
|
||||
}
|
||||
|
||||
@ -39,9 +39,10 @@ static void onToggleAutoConnect(lv_event_t* event) {
|
||||
}
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
const Bundle& bundle = app.getParameters();
|
||||
std::string ssid = bundle.getString("ssid");
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto paremeters = app.getParameters();
|
||||
tt_check(paremeters != nullptr, "No parameters");
|
||||
std::string ssid = paremeters->getString("ssid");
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::toolbar_create(parent, ssid);
|
||||
@ -69,7 +70,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6);
|
||||
|
||||
lv_obj_t* auto_connect_switch = lv_switch_create(wrapper);
|
||||
lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&bundle);
|
||||
lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&paremeters);
|
||||
lv_obj_align(auto_connect_switch, LV_ALIGN_TOP_RIGHT, 0, 0);
|
||||
|
||||
service::wifi::settings::WifiApSettings settings {};
|
||||
@ -85,7 +86,7 @@ static void onShow(App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "WifiApSettings",
|
||||
.name = "Wi-Fi AP Settings",
|
||||
.icon = LV_SYMBOL_WIFI,
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
#include "Parameters.h"
|
||||
#include "WifiConnect.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "lvgl.h"
|
||||
#include "service/gui/Gui.h"
|
||||
#include "service/wifi/WifiSettings.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
#include <TactilityCore.h>
|
||||
|
||||
namespace tt::app::wificonnect {
|
||||
|
||||
@ -107,7 +107,7 @@ void View::createBottomButtons(WifiConnect* wifi, lv_obj_t* parent) {
|
||||
}
|
||||
|
||||
// TODO: Standardize dialogs
|
||||
void View::init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent) {
|
||||
void View::init(AppContext& app, WifiConnect* wifiConnect, lv_obj_t* parent) {
|
||||
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lvgl::toolbar_create(parent, app);
|
||||
@ -187,15 +187,17 @@ void View::init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent) {
|
||||
service::gui::keyboardAddTextArea(password_textarea);
|
||||
|
||||
// Init from app parameters
|
||||
const Bundle& bundle = app.getParameters();
|
||||
std::string ssid;
|
||||
if (bundle.optString(WIFI_CONNECT_PARAM_SSID, ssid)) {
|
||||
lv_textarea_set_text(ssid_textarea, ssid.c_str());
|
||||
}
|
||||
auto bundle = app.getParameters();
|
||||
if (bundle != nullptr) {
|
||||
std::string ssid;
|
||||
if (bundle->optString(WIFI_CONNECT_PARAM_SSID, ssid)) {
|
||||
lv_textarea_set_text(ssid_textarea, ssid.c_str());
|
||||
}
|
||||
|
||||
std::string password;
|
||||
if (bundle.optString(WIFI_CONNECT_PARAM_PASSWORD, password)) {
|
||||
lv_textarea_set_text(password_textarea, password.c_str());
|
||||
std::string password;
|
||||
if (bundle->optString(WIFI_CONNECT_PARAM_PASSWORD, password)) {
|
||||
lv_textarea_set_text(password_textarea, password.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include "Bindings.h"
|
||||
#include "State.h"
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::app::wificonnect {
|
||||
@ -25,7 +25,7 @@ public:
|
||||
lv_obj_t* connection_error = nullptr;
|
||||
lv_group_t* group = nullptr;
|
||||
|
||||
void init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent);
|
||||
void init(AppContext& app, WifiConnect* wifiConnect, lv_obj_t* parent);
|
||||
void update(Bindings* bindings, State* state);
|
||||
|
||||
void createBottomButtons(WifiConnect* wifi, lv_obj_t* parent);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "WifiConnect.h"
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include "service/wifi/Wifi.h"
|
||||
@ -10,6 +10,18 @@ namespace tt::app::wificonnect {
|
||||
|
||||
#define TAG "wifi_connect"
|
||||
|
||||
extern const AppManifest manifest;
|
||||
|
||||
/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */
|
||||
std::shared_ptr<WifiConnect> _Nullable optWifiConnect() {
|
||||
app::AppContext* app = service::loader::getCurrentApp();
|
||||
if (app->getManifest().id == manifest.id) {
|
||||
return std::static_pointer_cast<WifiConnect>(app->getData());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void eventCallback(const void* message, void* context) {
|
||||
auto* event = static_cast<const service::wifi::WifiEvent*>(message);
|
||||
auto* wifi = static_cast<WifiConnect*>(context);
|
||||
@ -76,7 +88,7 @@ void WifiConnect::requestViewUpdate() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void WifiConnect::onShow(App& app, lv_obj_t* parent) {
|
||||
void WifiConnect::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lock();
|
||||
view_enabled = true;
|
||||
view.init(app, this, parent);
|
||||
@ -84,41 +96,35 @@ void WifiConnect::onShow(App& app, lv_obj_t* parent) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void WifiConnect::onHide(App& app) {
|
||||
void WifiConnect::onHide(AppContext& app) {
|
||||
// No need to lock view, as this is called from within Gui's LVGL context
|
||||
lock();
|
||||
view_enabled = false;
|
||||
unlock();
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
auto* wifi = static_cast<WifiConnect*>(app.getData());
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto wifi = std::static_pointer_cast<WifiConnect>(app.getData());
|
||||
wifi->onShow(app, parent);
|
||||
}
|
||||
|
||||
static void onHide(App& app) {
|
||||
auto* wifi = static_cast<WifiConnect*>(app.getData());
|
||||
static void onHide(AppContext& app) {
|
||||
auto wifi = std::static_pointer_cast<WifiConnect>(app.getData());
|
||||
wifi->onHide(app);
|
||||
}
|
||||
|
||||
static void onStart(App& app) {
|
||||
auto* wifi_connect = new WifiConnect();
|
||||
app.setData(wifi_connect);
|
||||
static void onStart(AppContext& app) {
|
||||
auto wifi = std::shared_ptr<WifiConnect>(new WifiConnect());
|
||||
app.setData(wifi);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
auto* wifi_connect = static_cast<WifiConnect*>(app.getData());
|
||||
tt_assert(wifi_connect != nullptr);
|
||||
delete wifi_connect;
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "WifiConnect",
|
||||
.name = "Wi-Fi Connect",
|
||||
.icon = LV_SYMBOL_WIFI,
|
||||
.type = TypeSettings,
|
||||
.onStart = &onStart,
|
||||
.onStop = &onStop,
|
||||
.onShow = &onShow,
|
||||
.onHide = &onHide
|
||||
};
|
||||
|
||||
@ -28,8 +28,8 @@ public:
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
void onShow(App& app, lv_obj_t* parent);
|
||||
void onHide(App& app);
|
||||
void onShow(AppContext& app, lv_obj_t* parent);
|
||||
void onHide(AppContext& app);
|
||||
|
||||
State& getState() { return state; }
|
||||
Bindings& getBindings() { return bindings; }
|
||||
|
||||
@ -214,7 +214,7 @@ void View::updateEnableOnBootToggle() {
|
||||
|
||||
// region Main
|
||||
|
||||
void View::init(const App& app, Bindings* bindings, lv_obj_t* parent) {
|
||||
void View::init(const AppContext& app, Bindings* bindings, lv_obj_t* parent) {
|
||||
root = parent;
|
||||
|
||||
// Toolbar
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "Bindings.h"
|
||||
#include "State.h"
|
||||
#include "lvgl.h"
|
||||
@ -16,7 +16,7 @@ private:
|
||||
lv_obj_t* networks_list = nullptr;
|
||||
public:
|
||||
View() {}
|
||||
void init(const App& app, Bindings* bindings, lv_obj_t* parent);
|
||||
void init(const AppContext& app, Bindings* bindings, lv_obj_t* parent);
|
||||
void update(Bindings* bindings, State* state);
|
||||
|
||||
private:
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include "View.h"
|
||||
#include "State.h"
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "app/wificonnect/Parameters.h"
|
||||
#include "app/wifiapsettings/WifiApSettings.h"
|
||||
#include "TactilityCore.h"
|
||||
@ -21,9 +21,9 @@ static void onConnect(const char* ssid) {
|
||||
service::wifi::connect(&settings, false);
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Starting connection dialog");
|
||||
Bundle bundle;
|
||||
bundle.putString(WIFI_CONNECT_PARAM_SSID, ssid);
|
||||
bundle.putString(WIFI_CONNECT_PARAM_PASSWORD, "");
|
||||
auto bundle = std::shared_ptr<Bundle>(new Bundle());
|
||||
bundle->putString(WIFI_CONNECT_PARAM_SSID, ssid);
|
||||
bundle->putString(WIFI_CONNECT_PARAM_PASSWORD, "");
|
||||
service::loader::startApp("WifiConnect", false, bundle);
|
||||
}
|
||||
}
|
||||
@ -95,7 +95,7 @@ static void wifiManageEventCallback(const void* message, void* context) {
|
||||
wifi->requestViewUpdate();
|
||||
}
|
||||
|
||||
void WifiManage::onShow(App& app, lv_obj_t* parent) {
|
||||
void WifiManage::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
PubSub* wifi_pubsub = service::wifi::getPubsub();
|
||||
wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &wifiManageEventCallback, this);
|
||||
|
||||
@ -121,7 +121,7 @@ void WifiManage::onShow(App& app, lv_obj_t* parent) {
|
||||
}
|
||||
}
|
||||
|
||||
void WifiManage::onHide(TT_UNUSED App& app) {
|
||||
void WifiManage::onHide(TT_UNUSED AppContext& app) {
|
||||
lock();
|
||||
PubSub* wifi_pubsub = service::wifi::getPubsub();
|
||||
tt_pubsub_unsubscribe(wifi_pubsub, wifiSubscription);
|
||||
@ -132,37 +132,29 @@ void WifiManage::onHide(TT_UNUSED App& app) {
|
||||
|
||||
// region Manifest methods
|
||||
|
||||
static void onStart(App& app) {
|
||||
auto* wifi = new WifiManage();
|
||||
static void onStart(AppContext& app) {
|
||||
auto wifi = std::shared_ptr<WifiManage>(new WifiManage());
|
||||
app.setData(wifi);
|
||||
}
|
||||
|
||||
static void onStop(App& app) {
|
||||
auto* wifi = (WifiManage*)app.getData();
|
||||
tt_assert(wifi != nullptr);
|
||||
delete wifi;
|
||||
}
|
||||
|
||||
static void onShow(App& app, lv_obj_t* parent) {
|
||||
auto* wifi = (WifiManage*)app.getData();
|
||||
static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto wifi = std::static_pointer_cast<WifiManage>(app.getData());
|
||||
wifi->onShow(app, parent);
|
||||
}
|
||||
|
||||
static void onHide(App& app) {
|
||||
auto* wifi = (WifiManage*)app.getData();
|
||||
static void onHide(AppContext& app) {
|
||||
auto wifi = std::static_pointer_cast<WifiManage>(app.getData());
|
||||
wifi->onHide(app);
|
||||
}
|
||||
|
||||
|
||||
// endregion
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const AppManifest manifest = {
|
||||
.id = "WifiManage",
|
||||
.name = "Wi-Fi",
|
||||
.icon = LV_SYMBOL_WIFI,
|
||||
.type = TypeSettings,
|
||||
.onStart = onStart,
|
||||
.onStop = onStop,
|
||||
.onShow = onShow,
|
||||
.onHide = onHide
|
||||
};
|
||||
|
||||
@ -23,8 +23,8 @@ public:
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
void onShow(App& app, lv_obj_t* parent);
|
||||
void onHide(App& app);
|
||||
void onShow(AppContext& app, lv_obj_t* parent);
|
||||
void onHide(AppContext& app);
|
||||
|
||||
State& getState() { return state; }
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const app::App& app) {
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const app::AppContext& app) {
|
||||
return toolbar_create(parent, app.getManifest().name);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
@ -19,7 +19,7 @@ typedef struct {
|
||||
} ToolbarAction;
|
||||
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title);
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const app::App& app);
|
||||
lv_obj_t* toolbar_create(lv_obj_t* parent, const app::AppContext& app);
|
||||
void toolbar_set_title(lv_obj_t* obj, const std::string& title);
|
||||
void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data);
|
||||
uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data);
|
||||
|
||||
@ -18,8 +18,8 @@ Gui* gui = nullptr;
|
||||
void loader_callback(const void* message, TT_UNUSED void* context) {
|
||||
auto* event = static_cast<const loader::LoaderEvent*>(message);
|
||||
if (event->type == loader::LoaderEventTypeApplicationShowing) {
|
||||
app::App& app = event->app_showing.app;
|
||||
const app::Manifest& app_manifest = app.getManifest();
|
||||
app::AppContext& app = event->app_showing.app;
|
||||
const app::AppManifest& app_manifest = app.getManifest();
|
||||
showApp(app, app_manifest.onShow, app_manifest.onHide);
|
||||
} else if (event->type == loader::LoaderEventTypeApplicationHiding) {
|
||||
hideApp();
|
||||
@ -77,7 +77,7 @@ void requestDraw() {
|
||||
thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
|
||||
}
|
||||
|
||||
void showApp(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) {
|
||||
void showApp(app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) {
|
||||
lock();
|
||||
tt_check(gui->app_view_port == nullptr);
|
||||
gui->app_view_port = view_port_alloc(app, on_show, on_hide);
|
||||
@ -128,14 +128,14 @@ static int32_t gui_main(TT_UNUSED void* p) {
|
||||
|
||||
// region AppManifest
|
||||
|
||||
static void start(TT_UNUSED Service& service) {
|
||||
static void start(TT_UNUSED ServiceContext& service) {
|
||||
gui = gui_alloc();
|
||||
|
||||
gui->thread->setPriority(THREAD_PRIORITY_SERVICE);
|
||||
gui->thread->start();
|
||||
}
|
||||
|
||||
static void stop(TT_UNUSED Service& service) {
|
||||
static void stop(TT_UNUSED ServiceContext& service) {
|
||||
lock();
|
||||
|
||||
ThreadId thread_id = gui->thread->getId();
|
||||
@ -148,7 +148,7 @@ static void stop(TT_UNUSED Service& service) {
|
||||
gui_free(gui);
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "Gui",
|
||||
.onStart = &start,
|
||||
.onStop = &stop
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "ViewPort.h"
|
||||
|
||||
namespace tt::service::gui {
|
||||
@ -14,7 +14,7 @@ typedef struct Gui Gui;
|
||||
* @param on_show
|
||||
* @param on_hide
|
||||
*/
|
||||
void showApp(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide);
|
||||
void showApp(app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide);
|
||||
|
||||
/**
|
||||
* Hide the current app's viewport.
|
||||
|
||||
@ -9,7 +9,7 @@ namespace tt::service::gui {
|
||||
|
||||
#define TAG "gui"
|
||||
|
||||
static lv_obj_t* create_app_views(Gui* gui, lv_obj_t* parent, app::App& app) {
|
||||
static lv_obj_t* create_app_views(Gui* gui, lv_obj_t* parent, app::AppContext& app) {
|
||||
lvgl::obj_set_style_bg_blacken(parent);
|
||||
|
||||
lv_obj_t* vertical_container = lv_obj_create(parent);
|
||||
@ -49,7 +49,7 @@ void redraw(Gui* gui) {
|
||||
|
||||
ViewPort* view_port = gui->app_view_port;
|
||||
if (view_port != nullptr) {
|
||||
app::App& app = view_port->app;
|
||||
app::AppContext& app = view_port->app;
|
||||
lv_obj_t* container = create_app_views(gui, gui->lvgl_parent, app);
|
||||
view_port_show(view_port, container);
|
||||
} else {
|
||||
|
||||
@ -9,7 +9,7 @@ namespace tt::service::gui {
|
||||
#define TAG "viewport"
|
||||
|
||||
ViewPort* view_port_alloc(
|
||||
app::App& app,
|
||||
app::AppContext& app,
|
||||
ViewPortShowCallback on_show,
|
||||
ViewPortHideCallback on_hide
|
||||
) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::service::gui {
|
||||
@ -8,18 +8,18 @@ namespace tt::service::gui {
|
||||
/** ViewPort Draw callback
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef void (*ViewPortShowCallback)(app::App& app, lv_obj_t* parent);
|
||||
typedef void (*ViewPortHideCallback)(app::App& app);
|
||||
typedef void (*ViewPortShowCallback)(app::AppContext& app, lv_obj_t* parent);
|
||||
typedef void (*ViewPortHideCallback)(app::AppContext& app);
|
||||
|
||||
// TODO: Move internally, use handle publicly
|
||||
|
||||
typedef struct ViewPort {
|
||||
app::App& app;
|
||||
app::AppContext& app;
|
||||
ViewPortShowCallback onShow;
|
||||
ViewPortHideCallback _Nullable onHide;
|
||||
|
||||
ViewPort(
|
||||
app::App& app,
|
||||
app::AppContext& app,
|
||||
ViewPortShowCallback on_show,
|
||||
ViewPortHideCallback _Nullable on_hide
|
||||
) : app(app), onShow(on_show), onHide(on_hide) {}
|
||||
@ -35,7 +35,7 @@ typedef struct ViewPort {
|
||||
* @return ViewPort instance
|
||||
*/
|
||||
ViewPort* view_port_alloc(
|
||||
app::App& app,
|
||||
app::AppContext& app,
|
||||
ViewPortShowCallback on_show,
|
||||
ViewPortHideCallback on_hide
|
||||
);
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#include "Tactility.h"
|
||||
#include <Mutex.h>
|
||||
#include "app/Manifest.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "app/ManifestRegistry.h"
|
||||
#include "service/Manifest.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
#include "service/gui/Gui.h"
|
||||
#include "service/loader/Loader_i.h"
|
||||
#include "RtosCompat.h"
|
||||
@ -63,7 +63,7 @@ static void loader_unlock() {
|
||||
tt_check(tt_mutex_release(loader_singleton->mutex) == TtStatusOk);
|
||||
}
|
||||
|
||||
LoaderStatus startApp(const std::string& id, bool blocking, const Bundle& arguments) {
|
||||
LoaderStatus startApp(const std::string& id, bool blocking, std::shared_ptr<const Bundle> parameters) {
|
||||
TT_LOG_I(TAG, "Start app %s", id.c_str());
|
||||
tt_assert(loader_singleton);
|
||||
|
||||
@ -71,7 +71,7 @@ LoaderStatus startApp(const std::string& id, bool blocking, const Bundle& argume
|
||||
.value = LoaderStatusOk
|
||||
};
|
||||
|
||||
auto* start_message = new LoaderMessageAppStart(id, arguments);
|
||||
auto* start_message = new LoaderMessageAppStart(id, parameters);
|
||||
LoaderMessage message(start_message, result);
|
||||
|
||||
EventFlag* event_flag = blocking ? new EventFlag() : nullptr;
|
||||
@ -99,12 +99,12 @@ void stopApp() {
|
||||
loader_singleton->queue.put(&message, TtWaitForever);
|
||||
}
|
||||
|
||||
app::App* _Nullable getCurrentApp() {
|
||||
app::AppContext* _Nullable getCurrentApp() {
|
||||
tt_assert(loader_singleton);
|
||||
loader_lock();
|
||||
app::AppInstance* app = loader_singleton->app_stack.top();
|
||||
loader_unlock();
|
||||
return dynamic_cast<app::App*>(app);
|
||||
return dynamic_cast<app::AppContext*>(app);
|
||||
}
|
||||
|
||||
PubSub* getPubsub() {
|
||||
@ -133,7 +133,7 @@ static const char* app_state_to_string(app::State state) {
|
||||
}
|
||||
|
||||
static void app_transition_to_state(app::AppInstance& app, app::State state) {
|
||||
const app::Manifest& manifest = app.getManifest();
|
||||
const app::AppManifest& manifest = app.getManifest();
|
||||
const app::State old_state = app.getState();
|
||||
|
||||
TT_LOG_I(
|
||||
@ -187,15 +187,15 @@ static void app_transition_to_state(app::AppInstance& app, app::State state) {
|
||||
}
|
||||
|
||||
static LoaderStatus loader_do_start_app_with_manifest(
|
||||
const app::Manifest* manifest,
|
||||
const Bundle& bundle
|
||||
const app::AppManifest* manifest,
|
||||
std::shared_ptr<const Bundle> _Nullable parameters
|
||||
) {
|
||||
TT_LOG_I(TAG, "start with manifest %s", manifest->id.c_str());
|
||||
|
||||
loader_lock();
|
||||
|
||||
auto previous_app = !loader_singleton->app_stack.empty() ? loader_singleton->app_stack.top() : nullptr;
|
||||
auto new_app = new app::AppInstance(*manifest, bundle);
|
||||
auto new_app = new app::AppInstance(*manifest, parameters);
|
||||
new_app->mutableFlags().showStatusbar = (manifest->type != app::TypeBoot);
|
||||
|
||||
loader_singleton->app_stack.push(new_app);
|
||||
@ -227,16 +227,16 @@ static LoaderStatus loader_do_start_app_with_manifest(
|
||||
|
||||
static LoaderStatus do_start_by_id(
|
||||
const std::string& id,
|
||||
const Bundle& bundle
|
||||
std::shared_ptr<const Bundle> _Nullable parameters
|
||||
) {
|
||||
TT_LOG_I(TAG, "Start by id %s", id.c_str());
|
||||
|
||||
const app::Manifest* manifest = app::findAppById(id);
|
||||
const app::AppManifest* manifest = app::findAppById(id);
|
||||
if (manifest == nullptr) {
|
||||
TT_LOG_E(TAG, "App not found: %s", id.c_str());
|
||||
return LoaderStatusErrorUnknownApp;
|
||||
} else {
|
||||
return loader_do_start_app_with_manifest(manifest, bundle);
|
||||
return loader_do_start_app_with_manifest(manifest, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +263,7 @@ static void do_stop_app() {
|
||||
|
||||
std::unique_ptr<app::ResultHolder> result_holder = std::move(app_to_stop->getResult());
|
||||
|
||||
const app::Manifest& manifest = app_to_stop->getManifest();
|
||||
const app::AppManifest& manifest = app_to_stop->getManifest();
|
||||
app_transition_to_state(*app_to_stop, app::StateHiding);
|
||||
app_transition_to_state(*app_to_stop, app::StateStopped);
|
||||
|
||||
@ -283,7 +283,7 @@ static void do_stop_app() {
|
||||
auto on_result = app_to_resume->getManifest().onResult;
|
||||
if (on_result != nullptr) {
|
||||
if (result_holder != nullptr) {
|
||||
Bundle* result_bundle = result_holder->resultData;
|
||||
auto result_bundle = result_holder->resultData.get();
|
||||
if (result_bundle != nullptr) {
|
||||
on_result(
|
||||
*app_to_resume,
|
||||
@ -335,7 +335,7 @@ static int32_t loader_main(TT_UNUSED void* parameter) {
|
||||
case LoaderMessageTypeAppStart:
|
||||
message.result.status_value.value = do_start_by_id(
|
||||
message.payload.start->id,
|
||||
message.payload.start->bundle
|
||||
message.payload.start->parameters
|
||||
);
|
||||
if (message.api_lock != nullptr) {
|
||||
message.api_lock->set(TT_API_LOCK_EVENT);
|
||||
@ -359,7 +359,7 @@ static int32_t loader_main(TT_UNUSED void* parameter) {
|
||||
|
||||
// region AppManifest
|
||||
|
||||
static void loader_start(TT_UNUSED Service& service) {
|
||||
static void loader_start(TT_UNUSED ServiceContext& service) {
|
||||
tt_check(loader_singleton == nullptr);
|
||||
loader_singleton = loader_alloc();
|
||||
|
||||
@ -367,7 +367,7 @@ static void loader_start(TT_UNUSED Service& service) {
|
||||
loader_singleton->thread->start();
|
||||
}
|
||||
|
||||
static void loader_stop(TT_UNUSED Service& service) {
|
||||
static void loader_stop(TT_UNUSED ServiceContext& service) {
|
||||
tt_check(loader_singleton != nullptr);
|
||||
|
||||
// Send stop signal to thread and wait for thread to finish
|
||||
@ -383,7 +383,7 @@ static void loader_stop(TT_UNUSED Service& service) {
|
||||
loader_singleton = nullptr;
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "Loader",
|
||||
.onStart = &loader_start,
|
||||
.onStop = &loader_stop
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/Manifest.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "Bundle.h"
|
||||
#include "Pubsub.h"
|
||||
#include "service/Manifest.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::service::loader {
|
||||
|
||||
@ -21,17 +22,17 @@ typedef enum {
|
||||
* @brief Start an app
|
||||
* @param[in] id application name or id
|
||||
* @param[in] blocking whether this call is blocking or not. You cannot call this from an LVGL thread.
|
||||
* @param[in] arguments optional parameters to pass onto the application
|
||||
* @param[in] parameters optional parameters to pass onto the application
|
||||
* @return LoaderStatus
|
||||
*/
|
||||
LoaderStatus startApp(const std::string& id, bool blocking = false, const Bundle& arguments = Bundle());
|
||||
LoaderStatus startApp(const std::string& id, bool blocking = false, std::shared_ptr<const Bundle> _Nullable parameters = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Stop the currently showing app. Show the previous app if any app was still running.
|
||||
*/
|
||||
void stopApp();
|
||||
|
||||
app::App* _Nullable getCurrentApp();
|
||||
app::AppContext* _Nullable getCurrentApp();
|
||||
|
||||
/**
|
||||
* @brief PubSub for LoaderEvent
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "ScreenshotTask.h"
|
||||
#include "service/Service.h"
|
||||
#include "service/ServiceContext.h"
|
||||
#include "service/ServiceRegistry.h"
|
||||
#include "TactilityCore.h"
|
||||
|
||||
@ -11,7 +11,7 @@ namespace tt::service::screenshot {
|
||||
|
||||
#define TAG "screenshot_service"
|
||||
|
||||
extern const Manifest manifest;
|
||||
extern const ServiceManifest manifest;
|
||||
|
||||
typedef struct {
|
||||
Mutex* mutex;
|
||||
@ -41,12 +41,12 @@ static void service_data_unlock(ServiceData* data) {
|
||||
tt_check(tt_mutex_release(data->mutex) == TtStatusOk);
|
||||
}
|
||||
|
||||
static void on_start(Service& service) {
|
||||
static void on_start(ServiceContext& service) {
|
||||
ServiceData* data = service_data_alloc();
|
||||
service.setData(data);
|
||||
}
|
||||
|
||||
static void on_stop(Service& service) {
|
||||
static void on_stop(ServiceContext& service) {
|
||||
auto* data = static_cast<ServiceData*>(service.getData());
|
||||
if (data->task) {
|
||||
task::free(data->task);
|
||||
@ -95,7 +95,7 @@ void startTimed(const char* path, uint8_t delay_in_seconds, uint8_t amount) {
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_Nullable Service* service = findServiceById(manifest.id);
|
||||
_Nullable ServiceContext* service = findServiceById(manifest.id);
|
||||
if (service == nullptr) {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
return;
|
||||
@ -132,7 +132,7 @@ bool isStarted() {
|
||||
return getMode() != ScreenshotModeNone;
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "Screenshot",
|
||||
.onStart = &on_start,
|
||||
.onStop = &on_stop
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "ScreenshotTask.h"
|
||||
#include "lv_screenshot.h"
|
||||
|
||||
#include "app/App.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "Mutex.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "Thread.h"
|
||||
@ -98,9 +98,9 @@ static int32_t screenshot_task(void* context) {
|
||||
break; // Interrupted loop
|
||||
}
|
||||
} else if (data->work.type == TASK_WORK_TYPE_APPS) {
|
||||
app::App* _Nullable app = loader::getCurrentApp();
|
||||
app::AppContext* _Nullable app = loader::getCurrentApp();
|
||||
if (app) {
|
||||
const app::Manifest& manifest = app->getManifest();
|
||||
const app::AppManifest& manifest = app->getManifest();
|
||||
if (manifest.id != last_app_id) {
|
||||
delay_ms(100);
|
||||
last_app_id = manifest.id;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include "hal/Power.h"
|
||||
#include "hal/sdcard/Sdcard.h"
|
||||
#include "Mutex.h"
|
||||
#include "service/Service.h"
|
||||
#include "service/ServiceContext.h"
|
||||
#include "service/wifi/Wifi.h"
|
||||
#include "Tactility.h"
|
||||
#include "lvgl/Statusbar.h"
|
||||
@ -184,7 +184,7 @@ int32_t service_main(TT_UNUSED void* parameter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void on_start(Service& service) {
|
||||
static void on_start(ServiceContext& service) {
|
||||
ServiceData* data = service_data_alloc();
|
||||
service.setData(data);
|
||||
|
||||
@ -195,7 +195,7 @@ static void on_start(Service& service) {
|
||||
data->thread->start();
|
||||
}
|
||||
|
||||
static void on_stop(Service& service) {
|
||||
static void on_stop(ServiceContext& service) {
|
||||
auto* data = static_cast<ServiceData*>(service.getData());
|
||||
|
||||
// Stop thread
|
||||
@ -208,7 +208,7 @@ static void on_stop(Service& service) {
|
||||
service_data_free(data);
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "Statusbar",
|
||||
.onStart = &on_start,
|
||||
.onStop = &on_stop
|
||||
|
||||
25
TactilityHeadless/Private/service/ServiceInstance.h
Normal file
25
TactilityHeadless/Private/service/ServiceInstance.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "service/ServiceContext.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
class ServiceInstance : public ServiceContext {
|
||||
|
||||
private:
|
||||
|
||||
Mutex mutex = Mutex(MutexTypeNormal);
|
||||
const service::ServiceManifest& manifest;
|
||||
void* data = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
explicit ServiceInstance(const service::ServiceManifest& manifest);
|
||||
~ServiceInstance() override = default;
|
||||
|
||||
[[nodiscard]] const service::ServiceManifest& getManifest() const override;
|
||||
[[nodiscard]] void* getData() const override;
|
||||
void setData(void* newData) override;
|
||||
};
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
#include "TactilityHeadless.h"
|
||||
#include "hal/Configuration.h"
|
||||
#include "hal/Hal_i.h"
|
||||
#include "service/Manifest.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
#include "service/ServiceRegistry.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
@ -12,10 +12,10 @@ namespace tt {
|
||||
|
||||
#define TAG "tactility"
|
||||
|
||||
namespace service::wifi { extern const Manifest manifest; }
|
||||
namespace service::sdcard { extern const Manifest manifest; }
|
||||
namespace service::wifi { extern const ServiceManifest manifest; }
|
||||
namespace service::sdcard { extern const ServiceManifest manifest; }
|
||||
|
||||
static const service::Manifest* const system_services[] = {
|
||||
static const service::ServiceManifest* const system_services[] = {
|
||||
&service::sdcard::manifest,
|
||||
&service::wifi::manifest
|
||||
};
|
||||
@ -24,7 +24,7 @@ static const hal::Configuration* hardwareConfig = nullptr;
|
||||
|
||||
static void register_and_start_system_services() {
|
||||
TT_LOG_I(TAG, "Registering and starting system services");
|
||||
int app_count = sizeof(system_services) / sizeof(service::Manifest*);
|
||||
int app_count = sizeof(system_services) / sizeof(service::ServiceManifest*);
|
||||
for (int i = 0; i < app_count; ++i) {
|
||||
addService(system_services[i]);
|
||||
tt_check(service::startService(system_services[i]->id));
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
#include "Service.h"
|
||||
#include "Manifest.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
Service::Service(const service::Manifest& manifest) : manifest(manifest) {}
|
||||
|
||||
const service::Manifest& Service::getManifest() const { return manifest; }
|
||||
|
||||
void* Service::getData() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
void* data_copy = data;
|
||||
mutex.release();
|
||||
return data_copy;
|
||||
}
|
||||
|
||||
void Service::setData(void* newData) {
|
||||
mutex.acquire(TtWaitForever);
|
||||
data = newData;
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "Manifest.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
class Service {
|
||||
private:
|
||||
Mutex mutex = Mutex(MutexTypeNormal);
|
||||
const service::Manifest& manifest;
|
||||
void* data = nullptr;
|
||||
|
||||
public:
|
||||
Service(const service::Manifest& manifest);
|
||||
|
||||
[[nodiscard]] const service::Manifest& getManifest() const;
|
||||
[[nodiscard]] void* getData() const;
|
||||
void setData(void* newData);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
21
TactilityHeadless/Source/service/ServiceContext.h
Normal file
21
TactilityHeadless/Source/service/ServiceContext.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "ServiceManifest.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
class ServiceContext {
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~ServiceContext() = default;
|
||||
|
||||
public:
|
||||
|
||||
[[nodiscard]] virtual const service::ServiceManifest& getManifest() const = 0;
|
||||
[[nodiscard]] virtual void* getData() const = 0;
|
||||
virtual void setData(void* newData) = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
22
TactilityHeadless/Source/service/ServiceInstance.cpp
Normal file
22
TactilityHeadless/Source/service/ServiceInstance.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "service/ServiceInstance.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
ServiceInstance::ServiceInstance(const service::ServiceManifest&manifest) : manifest(manifest) {}
|
||||
|
||||
const service::ServiceManifest& ServiceInstance::getManifest() const { return manifest; }
|
||||
|
||||
void* ServiceInstance::getData() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
void* data_copy = data;
|
||||
mutex.release();
|
||||
return data_copy;
|
||||
}
|
||||
|
||||
void ServiceInstance::setData(void* newData) {
|
||||
mutex.acquire(TtWaitForever);
|
||||
data = newData;
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,12 +4,12 @@
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
class Service;
|
||||
class ServiceContext;
|
||||
|
||||
typedef void (*ServiceOnStart)(Service& service);
|
||||
typedef void (*ServiceOnStop)(Service& service);
|
||||
typedef void (*ServiceOnStart)(ServiceContext& service);
|
||||
typedef void (*ServiceOnStop)(ServiceContext& service);
|
||||
|
||||
typedef struct Manifest {
|
||||
struct ServiceManifest {
|
||||
/**
|
||||
* The identifier by which the app is launched by the system and other apps.
|
||||
*/
|
||||
@ -25,6 +25,6 @@ typedef struct Manifest {
|
||||
*/
|
||||
const ServiceOnStop onStop = nullptr;
|
||||
|
||||
} Manifest;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -1,8 +1,8 @@
|
||||
#include "ServiceRegistry.h"
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "Service.h"
|
||||
#include "Manifest.h"
|
||||
#include "service/ServiceInstance.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
#include "TactilityCore.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@ -11,8 +11,8 @@ namespace tt::service {
|
||||
|
||||
#define TAG "service_registry"
|
||||
|
||||
typedef std::unordered_map<std::string, const Manifest*> ManifestMap;
|
||||
typedef std::unordered_map<std::string, Service*> ServiceInstanceMap;
|
||||
typedef std::unordered_map<std::string, const ServiceManifest*> ManifestMap;
|
||||
typedef std::unordered_map<std::string, ServiceInstance*> ServiceInstanceMap;
|
||||
|
||||
static ManifestMap service_manifest_map;
|
||||
static ServiceInstanceMap service_instance_map;
|
||||
@ -20,57 +20,57 @@ static ServiceInstanceMap service_instance_map;
|
||||
static Mutex manifest_mutex(MutexTypeNormal);
|
||||
static Mutex instance_mutex(MutexTypeNormal);
|
||||
|
||||
void addService(const Manifest* manifest) {
|
||||
TT_LOG_I(TAG, "adding %s", manifest->id.c_str());
|
||||
void addService(const ServiceManifest* manifest) {
|
||||
TT_LOG_I(TAG, "Adding %s", manifest->id.c_str());
|
||||
|
||||
manifest_mutex.acquire(TtWaitForever);
|
||||
service_manifest_map[manifest->id] = manifest;
|
||||
manifest_mutex.release();
|
||||
}
|
||||
|
||||
const Manifest* _Nullable findManifestId(const std::string& id) {
|
||||
const ServiceManifest* _Nullable findManifestId(const std::string& id) {
|
||||
manifest_mutex.acquire(TtWaitForever);
|
||||
auto iterator = service_manifest_map.find(id);
|
||||
_Nullable const Manifest * manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr;
|
||||
_Nullable const ServiceManifest * manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr;
|
||||
manifest_mutex.release();
|
||||
return manifest;
|
||||
}
|
||||
|
||||
static Service* _Nullable service_registry_find_instance_by_id(const std::string& id) {
|
||||
static ServiceInstance* _Nullable service_registry_find_instance_by_id(const std::string& id) {
|
||||
manifest_mutex.acquire(TtWaitForever);
|
||||
auto iterator = service_instance_map.find(id);
|
||||
_Nullable Service* service = iterator != service_instance_map.end() ? iterator->second : nullptr;
|
||||
_Nullable ServiceInstance* service = iterator != service_instance_map.end() ? iterator->second : nullptr;
|
||||
manifest_mutex.release();
|
||||
return service;
|
||||
}
|
||||
|
||||
// TODO: return proper error/status instead of BOOL
|
||||
// TODO: Return proper error/status instead of BOOL?
|
||||
bool startService(const std::string& id) {
|
||||
TT_LOG_I(TAG, "starting %s", id.c_str());
|
||||
const Manifest* manifest = findManifestId(id);
|
||||
TT_LOG_I(TAG, "Starting %s", id.c_str());
|
||||
const ServiceManifest* manifest = findManifestId(id);
|
||||
if (manifest == nullptr) {
|
||||
TT_LOG_E(TAG, "manifest not found for service %s", id.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* service = new Service(*manifest);
|
||||
auto* service = new ServiceInstance(*manifest);
|
||||
manifest->onStart(*service);
|
||||
|
||||
instance_mutex.acquire(TtWaitForever);
|
||||
service_instance_map[manifest->id] = service;
|
||||
instance_mutex.release();
|
||||
TT_LOG_I(TAG, "started %s", id.c_str());
|
||||
TT_LOG_I(TAG, "Started %s", id.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_Nullable Service* findServiceById(const std::string& service_id) {
|
||||
return (Service*)service_registry_find_instance_by_id(service_id);
|
||||
_Nullable ServiceContext* findServiceById(const std::string& service_id) {
|
||||
return static_cast<ServiceInstance*>(service_registry_find_instance_by_id(service_id));
|
||||
}
|
||||
|
||||
bool stopService(const std::string& id) {
|
||||
TT_LOG_I(TAG, "stopping %s", id.c_str());
|
||||
Service* service = service_registry_find_instance_by_id(id);
|
||||
TT_LOG_I(TAG, "Stopping %s", id.c_str());
|
||||
ServiceInstance* service = service_registry_find_instance_by_id(id);
|
||||
if (service == nullptr) {
|
||||
TT_LOG_W(TAG, "service not running: %s", id.c_str());
|
||||
return false;
|
||||
@ -83,7 +83,7 @@ bool stopService(const std::string& id) {
|
||||
service_instance_map.erase(id);
|
||||
instance_mutex.release();
|
||||
|
||||
TT_LOG_I(TAG, "stopped %s", id.c_str());
|
||||
TT_LOG_I(TAG, "Stopped %s", id.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Manifest.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
typedef void (*ManifestCallback)(const Manifest*, void* context);
|
||||
typedef void (*ManifestCallback)(const ServiceManifest*, void* context);
|
||||
|
||||
void initRegistry();
|
||||
|
||||
void addService(const Manifest* manifest);
|
||||
void removeService(const Manifest* manifest);
|
||||
void addService(const ServiceManifest* manifest);
|
||||
void removeService(const ServiceManifest* manifest);
|
||||
|
||||
bool startService(const std::string& id);
|
||||
bool stopService(const std::string& id);
|
||||
|
||||
const Manifest* _Nullable findManifestId(const std::string& id);
|
||||
Service* _Nullable findServiceById(const std::string& id);
|
||||
const ServiceManifest* _Nullable findManifestId(const std::string& id);
|
||||
ServiceContext* _Nullable findServiceById(const std::string& id);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "service/Service.h"
|
||||
#include "service/ServiceContext.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "TactilityHeadless.h"
|
||||
|
||||
@ -75,7 +75,7 @@ static int32_t sdcard_task(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void on_start(Service& service) {
|
||||
static void on_start(ServiceContext& service) {
|
||||
if (hal::getConfiguration().sdcard != nullptr) {
|
||||
ServiceData* data = service_data_alloc();
|
||||
service.setData(data);
|
||||
@ -85,7 +85,7 @@ static void on_start(Service& service) {
|
||||
}
|
||||
}
|
||||
|
||||
static void on_stop(Service& service) {
|
||||
static void on_stop(ServiceContext& service) {
|
||||
auto* data = static_cast<ServiceData*>(service.getData());
|
||||
if (data != nullptr) {
|
||||
service_data_lock(data);
|
||||
@ -98,7 +98,7 @@ static void on_stop(Service& service) {
|
||||
}
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "sdcard",
|
||||
.onStart = &on_start,
|
||||
.onStop = &on_stop
|
||||
|
||||
@ -6,10 +6,9 @@
|
||||
#include "Mutex.h"
|
||||
#include "Check.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "Log.h"
|
||||
#include "Pubsub.h"
|
||||
#include "service/Service.h"
|
||||
#include "service/ServiceContext.h"
|
||||
#include "WifiSettings.h"
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
@ -790,13 +789,13 @@ _Noreturn int32_t wifi_main(TT_UNUSED void* parameter) {
|
||||
}
|
||||
}
|
||||
|
||||
static void service_start(Service& service) {
|
||||
static void service_start(ServiceContext& service) {
|
||||
tt_assert(wifi_singleton == nullptr);
|
||||
wifi_singleton = new Wifi();
|
||||
service.setData(wifi_singleton);
|
||||
}
|
||||
|
||||
static void service_stop(Service& service) {
|
||||
static void service_stop(ServiceContext& service) {
|
||||
tt_assert(wifi_singleton != nullptr);
|
||||
|
||||
WifiRadioState state = wifi_singleton->radio_state;
|
||||
@ -812,7 +811,7 @@ static void service_stop(Service& service) {
|
||||
tt_crash("not fully implemented");
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "Wifi",
|
||||
.onStart = &service_start,
|
||||
.onStop = &service_stop
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
#include "MessageQueue.h"
|
||||
#include "Mutex.h"
|
||||
#include "Pubsub.h"
|
||||
#include "service/Service.h"
|
||||
#include "service/ServiceContext.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@ -175,12 +175,12 @@ static void unlock(Wifi* wifi) {
|
||||
}
|
||||
|
||||
|
||||
static void service_start(TT_UNUSED Service& service) {
|
||||
static void service_start(TT_UNUSED ServiceContext& service) {
|
||||
tt_check(wifi == nullptr);
|
||||
wifi = wifi_alloc();
|
||||
}
|
||||
|
||||
static void service_stop(TT_UNUSED Service& service) {
|
||||
static void service_stop(TT_UNUSED ServiceContext& service) {
|
||||
tt_check(wifi != nullptr);
|
||||
|
||||
wifi_free(wifi);
|
||||
@ -191,7 +191,7 @@ void wifi_main(void*) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
extern const Manifest manifest = {
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "Wifi",
|
||||
.onStart = &service_start,
|
||||
.onStop = &service_stop
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user