Ken Van Hoeylandt 08029a84dd
Various fixes and improvements (#269)
- Bump version for next release
- Fix default gamma for CYD 2432S032C
- Remember gamma curve setting from Display settings app
- Add UART to Core2 (still has no voltage on Grove port, though)
- LVGL performance improvements: pin to second core and set task priority to "critical"
- Fix build warnings, including deprecations
- Removed deprecated `Thread` constructor
- Fix WaveShare S3 display: Some displays would show a white screen at 12MHz, so I'm putting it back to the
official config values.
2025-04-01 23:42:56 +02:00

179 lines
4.6 KiB
C++

#include "Tactility/service/gui/Gui.h"
#include "Tactility/lvgl/LvglSync.h"
#include "Tactility/lvgl/Statusbar.h"
#include "Tactility/lvgl/Style.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/Tactility.h>
#include <Tactility/RtosCompat.h>
namespace tt::service::gui {
#define TAG "gui"
// Forward declarations
void redraw(Gui*);
static int32_t guiMain();
Gui* gui = nullptr;
void onLoaderMessage(const void* message, TT_UNUSED void* context) {
auto* event = static_cast<const loader::LoaderEvent*>(message);
if (event->type == loader::LoaderEventTypeApplicationShowing) {
auto app_instance = app::getCurrentAppContext();
showApp(app_instance);
} else if (event->type == loader::LoaderEventTypeApplicationHiding) {
hideApp();
}
}
Gui* gui_alloc() {
auto* instance = new Gui();
tt_check(instance != nullptr);
instance->thread = new Thread(
"gui",
4096, // Last known minimum was 2800 for launching desktop
[]() { return guiMain(); }
);
instance->loader_pubsub_subscription = loader::getPubsub()->subscribe(&onLoaderMessage, instance);
tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS));
instance->keyboardGroup = lv_group_create();
auto* screen_root = lv_scr_act();
assert(screen_root != nullptr);
lvgl::obj_set_style_bg_blacken(screen_root);
lv_obj_t* vertical_container = lv_obj_create(screen_root);
lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_all(vertical_container, 0, 0);
lv_obj_set_style_pad_gap(vertical_container, 0, 0);
lvgl::obj_set_style_bg_blacken(vertical_container);
instance->statusbarWidget = lvgl::statusbar_create(vertical_container);
auto* app_container = lv_obj_create(vertical_container);
lv_obj_set_style_pad_all(app_container, 0, 0);
lv_obj_set_style_border_width(app_container, 0, 0);
lvgl::obj_set_style_bg_blacken(app_container);
lv_obj_set_width(app_container, LV_PCT(100));
lv_obj_set_flex_grow(app_container, 1);
lv_obj_set_flex_flow(app_container, LV_FLEX_FLOW_COLUMN);
instance->appRootWidget = app_container;
lvgl::unlock();
return instance;
}
void gui_free(Gui* instance) {
assert(instance != nullptr);
delete instance->thread;
lv_group_delete(instance->keyboardGroup);
tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS));
lv_group_del(instance->keyboardGroup);
lvgl::unlock();
delete instance;
}
void lock() {
assert(gui);
tt_check(gui->mutex.lock(configTICK_RATE_HZ));
}
void unlock() {
assert(gui);
tt_check(gui->mutex.unlock());
}
void requestDraw() {
assert(gui);
ThreadId thread_id = gui->thread->getId();
Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW);
}
void showApp(std::shared_ptr<app::AppContext> app) {
lock();
tt_check(gui->appToRender == nullptr);
gui->appToRender = std::move(app);
unlock();
requestDraw();
}
void hideApp() {
lock();
tt_check(gui->appToRender != nullptr);
// We must lock the LVGL port, because the viewport hide callbacks
// might call LVGL APIs (e.g. to remove the keyboard from the screen root)
tt_check(lvgl::lock(configTICK_RATE_HZ));
gui->appToRender->getApp()->onHide(*gui->appToRender);
lvgl::unlock();
gui->appToRender = nullptr;
unlock();
}
static int32_t guiMain() {
tt_check(gui);
Gui* local_gui = gui;
while (true) {
uint32_t flags = Thread::awaitFlags(GUI_THREAD_FLAG_ALL, EventFlag::WaitAny, (uint32_t)portMAX_DELAY);
// Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) {
Thread::clearFlags(GUI_THREAD_FLAG_DRAW);
redraw(local_gui);
}
if (flags & GUI_THREAD_FLAG_EXIT) {
Thread::clearFlags(GUI_THREAD_FLAG_EXIT);
break;
}
}
return 0;
}
// region AppManifest
class GuiService : public Service {
public:
void onStart(TT_UNUSED ServiceContext& service) override {
assert(gui == nullptr);
gui = gui_alloc();
gui->thread->setPriority(THREAD_PRIORITY_SERVICE);
gui->thread->start();
}
void onStop(TT_UNUSED ServiceContext& service) override {
assert(gui != nullptr);
lock();
ThreadId thread_id = gui->thread->getId();
Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT);
gui->thread->join();
delete gui->thread;
unlock();
gui_free(gui);
}
};
extern const ServiceManifest manifest = {
.id = "Gui",
.createService = create<GuiService>
};
// endregion
} // namespace