Added Lilygo T-Deck support & more (#4)

* added lilygo t-deck

restructured boards
implemented HardwareConfig
implemented lilygo t-deck lcd and touch drivers
added sdkconfig defaults for supported boards

* cleanup

* added esp32s3 job

* build job names updated

* wip

* partial revert

* update readme and build.yml

* updated build.yaml with fix for quotes

* use esp-idf 5.1.2

* improvements and fixes

* fixes for display code

* made config const

* various improvements
This commit is contained in:
Ken Van Hoeylandt
2024-01-05 17:01:39 +01:00
committed by GitHub
parent eed990217f
commit 8336316133
53 changed files with 747 additions and 551 deletions
@@ -1,7 +1,6 @@
#include "desktop.h"
#include "lvgl.h"
#include "check.h"
#include "record.h"
#include "apps/services/loader/loader.h"
#include "apps/services/gui/gui.h"
#include "apps/services/gui/view_port.h"
@@ -11,9 +10,7 @@ static void on_open_app(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const AppManifest* manifest = lv_event_get_user_data(e);
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
loader_start_app_nonblocking(loader, manifest->id, NULL);
})
loader_start_app_nonblocking(manifest->id, NULL);
}
}
@@ -38,9 +35,7 @@ static void desktop_show(lv_obj_t* parent, void* context) {
static void desktop_start() {
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, &desktop_show, NULL);
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_add_view_port(gui, view_port, GuiLayerDesktop);
})
gui_add_view_port(view_port, GuiLayerDesktop);
}
static void desktop_stop() {
@@ -54,6 +49,5 @@ const AppManifest desktop_app = {
.type = AppTypeDesktop,
.on_start = &desktop_start,
.on_stop = &desktop_stop,
.on_show = NULL,
.stack_size = AppStackSizeNormal
.on_show = NULL
};
+86 -197
View File
@@ -4,192 +4,36 @@
#include "gui_i.h"
#include "log.h"
#include "record.h"
#include "kernel.h"
#define TAG "gui"
// Forward declarations from gui_draw.c
// Forward declarations
bool gui_redraw_fs(Gui*);
void gui_redraw(Gui*);
static int32_t gui_main(void*);
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
// Iterating backward
ViewPortArray_it_t it;
ViewPortArray_it_last(it, array);
while (!ViewPortArray_end_p(it)) {
ViewPort* view_port = *ViewPortArray_ref(it);
if (view_port_is_enabled(view_port)) {
ViewPort* view_port = *ViewPortArray_ref(it);
return view_port;
}
ViewPortArray_previous(it);
}
return NULL;
}
size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) {
furi_assert(gui);
furi_check(layer < GuiLayerMAX);
size_t ret = 0;
gui_lock(gui);
ViewPortArray_it_t it;
ViewPortArray_it_last(it, gui->layers[layer]);
while (!ViewPortArray_end_p(it)) {
ViewPort* view_port = *ViewPortArray_ref(it);
if (view_port_is_enabled(view_port)) {
ret++;
}
ViewPortArray_previous(it);
}
gui_unlock(gui);
return ret;
}
void gui_update(Gui* gui) {
furi_assert(gui);
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
}
void gui_lock(Gui* gui) {
furi_assert(gui);
furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk);
}
void gui_unlock(Gui* gui) {
furi_assert(gui);
furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk);
}
void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
furi_assert(gui);
furi_assert(view_port);
furi_check(layer < GuiLayerMAX);
gui_lock(gui);
// Verify that view port is not yet added
ViewPortArray_it_t it;
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_it(it, gui->layers[i]);
while (!ViewPortArray_end_p(it)) {
furi_assert(*ViewPortArray_ref(it) != view_port);
ViewPortArray_next(it);
}
}
// Add view port and link with gui
ViewPortArray_push_back(gui->layers[layer], view_port);
view_port_gui_set(view_port, gui);
gui_unlock(gui);
// Request redraw
gui_update(gui);
}
void gui_remove_view_port(Gui* gui, ViewPort* view_port) {
furi_assert(gui);
furi_assert(view_port);
gui_lock(gui);
view_port_gui_set(view_port, NULL);
ViewPortArray_it_t it;
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_it(it, gui->layers[i]);
while (!ViewPortArray_end_p(it)) {
if (*ViewPortArray_ref(it) == view_port) {
ViewPortArray_remove(gui->layers[i], it);
} else {
ViewPortArray_next(it);
}
}
}
/*
if(gui->ongoing_input_view_port == view_port) {
gui->ongoing_input_view_port = NULL;
}
*/
gui_unlock(gui);
// Request redraw
gui_update(gui);
}
void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) {
furi_assert(gui);
furi_assert(view_port);
gui_lock(gui);
// Remove
GuiLayer layer = GuiLayerMAX;
ViewPortArray_it_t it;
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_it(it, gui->layers[i]);
while (!ViewPortArray_end_p(it)) {
if (*ViewPortArray_ref(it) == view_port) {
ViewPortArray_remove(gui->layers[i], it);
furi_assert(layer == GuiLayerMAX);
layer = i;
} else {
ViewPortArray_next(it);
}
}
}
furi_assert(layer != GuiLayerMAX);
// Return to the top
ViewPortArray_push_back(gui->layers[layer], view_port);
gui_unlock(gui);
// Request redraw
gui_update(gui);
}
void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) {
furi_assert(gui);
furi_assert(view_port);
gui_lock(gui);
// Remove
GuiLayer layer = GuiLayerMAX;
ViewPortArray_it_t it;
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_it(it, gui->layers[i]);
while (!ViewPortArray_end_p(it)) {
if (*ViewPortArray_ref(it) == view_port) {
ViewPortArray_remove(gui->layers[i], it);
furi_assert(layer == GuiLayerMAX);
layer = i;
} else {
ViewPortArray_next(it);
}
}
}
furi_assert(layer != GuiLayerMAX);
// Return to the top
ViewPortArray_push_at(gui->layers[layer], 0, view_port);
gui_unlock(gui);
// Request redraw
gui_update(gui);
}
static Gui* gui = NULL;
Gui* gui_alloc() {
Gui* gui = malloc(sizeof(Gui));
gui->thread = furi_thread_alloc_ex(
Gui* instance = malloc(sizeof(Gui));
memset(instance, 0, sizeof(Gui));
furi_check(instance != NULL);
instance->thread = furi_thread_alloc_ex(
"gui",
2048,
AppStackSizeLarge, // Last known minimum was 2800 for launching desktop
&gui_main,
NULL
);
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
instance->mutex = xSemaphoreCreateRecursiveMutex();
furi_check(lvgl_port_lock(100));
gui->lvgl_parent = lv_scr_act();
instance->lvgl_parent = lv_scr_act();
lvgl_port_unlock();
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_init(gui->layers[i]);
instance->layers[i] = NULL;
}
/*
@@ -200,25 +44,72 @@ Gui* gui_alloc() {
furi_check(gui->input_events);
furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui);
*/
return gui;
return instance;
}
void gui_free(Gui* gui) {
furi_thread_free(gui->thread);
void gui_free(Gui* instance) {
furi_assert(instance != NULL);
furi_thread_free(instance->thread);
furi_mutex_free(instance->mutex);
free(instance);
}
if (gui->mutex) {
furi_mutex_free(gui->mutex);
}
void gui_lock() {
furi_assert(gui);
furi_assert(gui->mutex);
furi_check(xSemaphoreTakeRecursive(gui->mutex, portMAX_DELAY) == pdPASS);
}
void gui_unlock() {
furi_assert(gui);
furi_assert(gui->mutex);
furi_check(xSemaphoreGiveRecursive(gui->mutex) == pdPASS);
}
void gui_request_draw() {
furi_assert(gui);
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
}
void gui_add_view_port(ViewPort* view_port, GuiLayer layer) {
furi_assert(gui);
furi_assert(view_port);
furi_check(layer < GuiLayerMAX);
gui_lock();
furi_check(gui->layers[layer] == NULL, "layer in use");
gui->layers[layer] = view_port;
view_port_gui_set(view_port, gui);
gui_unlock();
gui_request_draw();
}
void gui_remove_view_port(ViewPort* view_port) {
furi_assert(gui);
furi_assert(view_port);
gui_lock();
view_port_gui_set(view_port, NULL);
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_clear(gui->layers[i]);
if (gui->layers[i] == view_port) {
gui->layers[i] = NULL;
break;
}
}
free(gui);
gui_unlock();
gui_request_draw();
}
static int32_t gui_main(void* parameter) {
UNUSED(parameter);
static int32_t gui_main(void* p) {
UNUSED(p);
furi_check(gui);
Gui* local_gui = gui;
while (1) {
uint32_t flags = furi_thread_flags_wait(
@@ -236,11 +127,10 @@ static int32_t gui_main(void* parameter) {
}*/
// Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) {
FURI_LOG_D(TAG, "redraw requested");
furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW);
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_redraw(gui);
})
gui_lock();
gui_redraw(local_gui);
gui_unlock();
}
if (flags & GUI_THREAD_FLAG_EXIT) {
@@ -252,29 +142,27 @@ static int32_t gui_main(void* parameter) {
return 0;
}
// region AppManifest
static void gui_start(void* parameter) {
UNUSED(parameter);
Gui* gui = gui_alloc();
furi_record_create(RECORD_GUI, gui);
furi_thread_set_priority(gui->thread, FuriThreadPriorityHigh);
gui = gui_alloc();
furi_thread_set_priority(gui->thread, FuriThreadPriorityNormal);
furi_thread_start(gui->thread);
}
static void gui_stop() {
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_lock(gui);
gui_lock();
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
furi_thread_join(gui->thread);
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
furi_thread_join(gui->thread);
gui_unlock(gui);
gui_unlock();
gui_free(gui);
})
furi_record_destroy(RECORD_GUI);
gui_free(gui);
}
const AppManifest gui_app = {
@@ -284,6 +172,7 @@ const AppManifest gui_app = {
.type = AppTypeService,
.on_start = &gui_start,
.on_stop = &gui_stop,
.on_show = NULL,
.stack_size = AppStackSizeNormal
.on_show = NULL
};
// endregion
@@ -12,26 +12,11 @@ extern const AppManifest gui_app;
/** Gui layers */
typedef enum {
GuiLayerDesktop, /**< Desktop layer for internal use. Like fullscreen but with status bar */
GuiLayerWindow, /**< Window layer, status bar is shown */
GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */
GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */
GuiLayerFullscreen, /**< Fullscreen layer, no status bar */
GuiLayerMAX /**< Don't use or move, special value */
} GuiLayer;
/** Gui Canvas Commit Callback */
typedef void (*GuiCanvasCommitCallback)(
uint8_t* data,
size_t size,
void* context
);
#define RECORD_GUI "gui"
typedef struct Gui Gui;
/** Add view_port to view_port tree
@@ -42,7 +27,7 @@ typedef struct Gui Gui;
* @param view_port ViewPort instance
* @param[in] layer GuiLayer where to place view_port
*/
void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer);
void gui_add_view_port(ViewPort* view_port, GuiLayer layer);
/** Remove view_port from rendering tree
*
@@ -51,25 +36,7 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer);
* @param gui Gui instance
* @param view_port ViewPort instance
*/
void gui_remove_view_port(Gui* gui, ViewPort* view_port);
/** Send ViewPort to the front
*
* Places selected ViewPort to the top of the drawing stack
*
* @param gui Gui instance
* @param view_port ViewPort instance
*/
void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port);
/** Send ViewPort to the back
*
* Places selected ViewPort to the bottom of the drawing stack
*
* @param gui Gui instance
* @param view_port ViewPort instance
*/
void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port);
void gui_remove_view_port(ViewPort* view_port);
#ifdef __cplusplus
}
@@ -39,12 +39,10 @@ static lv_obj_t* screen_with_top_bar_and_toolbar(lv_obj_t* parent) {
top_bar(vertical_container);
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
const AppManifest* manifest = loader_get_current_app(loader);
if (manifest != NULL) {
toolbar(vertical_container, TOP_BAR_HEIGHT, manifest);
}
})
const AppManifest* manifest = loader_get_current_app();
if (manifest != NULL) {
toolbar(vertical_container, TOP_BAR_HEIGHT, manifest);
}
lv_obj_t* spacer = lv_obj_create(vertical_container);
lv_obj_set_size(spacer, 2, 2);
@@ -60,7 +58,7 @@ static lv_obj_t* screen_with_top_bar_and_toolbar(lv_obj_t* parent) {
}
static bool gui_redraw_window(Gui* gui) {
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
ViewPort* view_port = gui->layers[GuiLayerWindow];
if (view_port) {
lv_obj_t* container = screen_with_top_bar_and_toolbar(gui->lvgl_parent);
view_port_draw(view_port, container);
@@ -71,7 +69,7 @@ static bool gui_redraw_window(Gui* gui) {
}
static bool gui_redraw_desktop(Gui* gui) {
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
ViewPort* view_port = gui->layers[GuiLayerDesktop];
if (view_port) {
lv_obj_t* container = screen_with_top_bar(gui->lvgl_parent);
view_port_draw(view_port, container);
@@ -84,7 +82,7 @@ static bool gui_redraw_desktop(Gui* gui) {
}
bool gui_redraw_fs(Gui* gui) {
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
ViewPort* view_port = gui->layers[GuiLayerFullscreen];
if (view_port) {
view_port_draw(view_port, gui->lvgl_parent);
return true;
@@ -95,7 +93,6 @@ bool gui_redraw_fs(Gui* gui) {
void gui_redraw(Gui* gui) {
furi_assert(gui);
gui_lock(gui);
furi_check(lvgl_port_lock(100));
lv_obj_clean(gui->lvgl_parent);
@@ -107,6 +104,4 @@ void gui_redraw(Gui* gui) {
}
lvgl_port_unlock();
gui_unlock(gui);
}
@@ -1,57 +1,28 @@
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "gui.h"
#include <m-algo.h>
#include <m-array.h>
#include <stdio.h>
#include "message_queue.h"
#include "mutex.h"
#include "pubsub.h"
#include "view_port.h"
#include "view_port_i.h"
#define GUI_DISPLAY_WIDTH 128
#define GUI_DISPLAY_HEIGHT 64
#define GUI_STATUS_BAR_X 0
#define GUI_STATUS_BAR_Y 0
#define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH
/* 0-1 pixels for upper thin frame
* 2-9 pixels for icons (battery, sd card, etc)
* 10-12 pixels for lower bold line */
#define GUI_STATUS_BAR_HEIGHT 13
/* icon itself area (battery, sd card, etc) excluding frame.
* painted 2 pixels below GUI_STATUS_BAR_X.
*/
#define GUI_STATUS_BAR_WORKAREA_HEIGHT 8
#define GUI_WINDOW_X 0
#define GUI_WINDOW_Y GUI_STATUS_BAR_HEIGHT
#define GUI_WINDOW_WIDTH GUI_DISPLAY_WIDTH
#define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y)
#include <stdio.h>
#define GUI_THREAD_FLAG_DRAW (1 << 0)
#define GUI_THREAD_FLAG_INPUT (1 << 1)
#define GUI_THREAD_FLAG_EXIT (1 << 2)
#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT)
ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST);
typedef struct {
GuiCanvasCommitCallback callback;
void* context;
} CanvasCallbackPair;
/** Gui structure */
struct Gui {
// Thread and lock
FuriThread* thread;
FuriMutex* mutex;
SemaphoreHandle_t mutex;
// Layers and Canvas
ViewPortArray_t layers[GuiLayerMAX];
ViewPort* layers[GuiLayerMAX];
lv_obj_t* lvgl_parent;
// Input
@@ -63,19 +34,11 @@ struct Gui {
*/
};
/** Find enabled ViewPort in ViewPortArray
*
* @param[in] array The ViewPortArray instance
*
* @return ViewPort instance or NULL
*/
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array);
/** Update GUI, request redraw
*
* @param gui Gui instance
*/
void gui_update(Gui* gui);
void gui_request_draw();
///** Input event callback
// *
@@ -86,21 +49,14 @@ void gui_update(Gui* gui);
// */
//void gui_input_events_callback(const void* value, void* ctx);
/** Get count of view ports in layer
*
* @param gui The Gui instance
* @param[in] layer GuiLayer that we want to get count of view ports
*/
size_t gui_active_view_port_count(Gui* gui, GuiLayer layer);
/** Lock GUI
*
* @param gui The Gui instance
*/
void gui_lock(Gui* gui);
void gui_lock();
/** Unlock GUI
*
* @param gui The Gui instance
*/
void gui_unlock(Gui* gui);
void gui_unlock();
@@ -36,7 +36,7 @@ void view_port_enabled_set(ViewPort* view_port, bool enabled) {
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
if (view_port->is_enabled != enabled) {
view_port->is_enabled = enabled;
if (view_port->gui) gui_update(view_port->gui);
if (view_port->gui) gui_request_draw();
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
}
@@ -66,7 +66,7 @@ void view_port_update(ViewPort* view_port) {
ESP_LOGW(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
}
if (view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
if (view_port->gui && view_port->is_enabled) gui_request_draw();
furi_mutex_release(view_port->mutex);
}
@@ -4,9 +4,7 @@
#include "apps/services/loader/loader.h"
static void app_toolbar_close(lv_event_t* event) {
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
loader_stop_app(loader);
})
loader_stop_app();
}
void toolbar(lv_obj_t* parent, lv_coord_t offset_y, const AppManifest* manifest) {
@@ -6,18 +6,55 @@
#include <sys/cdefs.h>
#include "esp_heap_caps.h"
#include "apps/services/gui/gui.h"
#include "freertos/semphr.h"
#define TAG "Loader"
// Forward declarations
static int32_t loader_main(void* p);
static LoaderStatus loader_do_start_by_id(
Loader* loader,
const char* id,
const char* args,
FuriString* error_message
);
LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args, FuriString* error_message) {
static Loader* loader = NULL;
static Loader* loader_alloc() {
furi_check(loader == NULL);
loader = malloc(sizeof(Loader));
loader->pubsub = furi_pubsub_alloc();
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
loader->thread = furi_thread_alloc_ex(
"loader",
AppStackSizeLarge, // Last known minimum was 2400 for starting Hello World app
&loader_main,
NULL
);
loader->app_data.args = NULL;
loader->app_data.app = NULL;
loader->app_data.view_port = NULL;
loader->mutex = xSemaphoreCreateRecursiveMutex();
return loader;
}
static void loader_free() {
furi_check(loader != NULL);
furi_thread_free(loader->thread);
furi_pubsub_free(loader->pubsub);
furi_message_queue_free(loader->queue);
furi_mutex_free(loader->mutex);
free(loader);
}
void loader_lock() {
furi_assert(loader);
furi_assert(loader->mutex);
furi_check(xSemaphoreTakeRecursive(loader->mutex, portMAX_DELAY) == pdPASS);
}
void loader_unlock() {
furi_assert(loader);
furi_assert(loader->mutex);
furi_check(xSemaphoreGiveRecursive(loader->mutex) == pdPASS);
}
LoaderStatus loader_start_app(const char* id, const char* args, FuriString* error_message) {
LoaderMessage message;
LoaderMessageLoaderStatusResult result;
@@ -32,7 +69,7 @@ LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args,
return result.value;
}
void loader_start_app_nonblocking(Loader* loader, const char* id, const char* args) {
void loader_start_app_nonblocking(const char* id, const char* args) {
LoaderMessage message;
LoaderMessageLoaderStatusResult result;
@@ -45,22 +82,22 @@ void loader_start_app_nonblocking(Loader* loader, const char* id, const char* ar
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
void loader_stop_app(Loader* loader) {
void loader_stop_app() {
LoaderMessage message;
message.type = LoaderMessageTypeAppStop;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
const AppManifest* _Nullable loader_get_current_app(Loader* loader) {
App* app = loader->app_data.app;
if (app != NULL) {
return app->manifest;
} else {
return NULL;
}
const AppManifest* _Nullable loader_get_current_app() {
loader_lock();
const App* app = loader->app_data.app;
const AppManifest* manifest = app ? app->manifest : NULL;
loader_unlock();
return manifest;
}
FuriPubSub* loader_get_pubsub(Loader* loader) {
FuriPubSub* loader_get_pubsub() {
furi_assert(loader);
// it's safe to return pubsub without locking
// because it's never freed and loader is never exited
@@ -68,30 +105,6 @@ FuriPubSub* loader_get_pubsub(Loader* loader) {
return loader->pubsub;
}
// implementation
static Loader* loader_alloc() {
Loader* loader = malloc(sizeof(Loader));
loader->pubsub = furi_pubsub_alloc();
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
loader->thread = furi_thread_alloc_ex(
"loader",
2048,
&loader_main,
NULL
);
loader->app_data.args = NULL;
loader->app_data.app = NULL;
return loader;
}
static void loader_free(Loader* loader) {
furi_pubsub_free(loader->pubsub);
furi_message_queue_free(loader->queue);
furi_thread_free(loader->thread);
free(loader);
}
static void loader_log_status_error(
LoaderStatus status,
FuriString* error_message,
@@ -128,15 +141,19 @@ static LoaderStatus loader_make_success_status(FuriString* error_message) {
}
static void loader_start_app_with_manifest(
Loader* loader,
const AppManifest* _Nonnull manifest,
const char* args
) {
FURI_LOG_I(TAG, "start with manifest %s", manifest->id);
if (manifest->type != AppTypeUser && manifest->type != AppTypeSystem) {
furi_crash("app type not supported by loader");
furi_crash("App type not supported by loader");
}
App* _Nonnull app = furi_app_alloc(manifest);
loader_lock();
loader->app_data.app = app;
loader->app_data.args = (void*)args;
@@ -149,23 +166,20 @@ static void loader_start_app_with_manifest(
loader->app_data.view_port = view_port;
view_port_draw_callback_set(view_port, manifest->on_show, NULL);
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_add_view_port(gui, view_port, GuiLayerWindow);
})
gui_add_view_port(view_port, GuiLayerWindow);
} else {
loader->app_data.view_port = NULL;
}
loader_unlock();
}
// process messages
static LoaderStatus loader_do_start_by_id(
Loader* loader,
const char* id,
const char* args,
FuriString* _Nullable error_message
) {
FURI_LOG_I(TAG, "loader start by id %s", id);
FURI_LOG_I(TAG, "Start by id %s", id);
const AppManifest* manifest = app_manifest_registry_find_by_id(id);
if (manifest == NULL) {
@@ -177,11 +191,13 @@ static LoaderStatus loader_do_start_by_id(
);
}
loader_start_app_with_manifest(loader, manifest, args);
loader_start_app_with_manifest(manifest, args);
return loader_make_success_status(error_message);
}
static void loader_do_stop_app(Loader* loader) {
static void loader_do_stop_app() {
loader_lock();
App* app = loader->app_data.app;
if (app == NULL) {
FURI_LOG_W(TAG, "Stop app: no app running");
@@ -192,9 +208,7 @@ static void loader_do_stop_app(Loader* loader) {
ViewPort* view_port = loader->app_data.view_port;
if (view_port) {
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_remove_view_port(gui, view_port);
})
gui_remove_view_port(view_port);
view_port_free(view_port);
loader->app_data.view_port = NULL;
}
@@ -211,6 +225,8 @@ static void loader_do_stop_app(Loader* loader) {
furi_app_free(loader->app_data.app);
loader->app_data.app = NULL;
loader_unlock();
FURI_LOG_I(
TAG,
"Application stopped. Free heap: %zu",
@@ -222,43 +238,38 @@ static void loader_do_stop_app(Loader* loader) {
furi_pubsub_publish(loader->pubsub, &event);
}
// app
bool loader_is_app_running() {
loader_lock();
bool is_running = loader->app_data.app != NULL;
loader_unlock();
return is_running;
}
static int32_t loader_main(void* p) {
UNUSED(p);
FuriMessageQueue* queue = NULL;
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
queue = loader->queue;
})
furi_check(queue != NULL);
LoaderMessage message;
bool exit_requested = false;
while (!exit_requested) {
if (furi_message_queue_get(queue, &message, FuriWaitForever) == FuriStatusOk) {
FURI_LOG_I(TAG, "processing message of type %d", message.type);
furi_check(loader != NULL);
if (furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
FURI_LOG_I(TAG, "Processing message of type %d", message.type);
switch (message.type) {
case LoaderMessageTypeStartByName:
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
if (loader->app_data.app) {
loader_do_stop_app(loader);
}
message.status_value->value = loader_do_start_by_id(
loader,
message.start.id,
message.start.args,
message.start.error_message
);
if (message.api_lock) {
api_lock_unlock(message.api_lock);
}
})
if (loader_is_app_running()) {
loader_do_stop_app();
}
message.status_value->value = loader_do_start_by_id(
message.start.id,
message.start.args,
message.start.error_message
);
if (message.api_lock) {
api_lock_unlock(message.api_lock);
}
break;
case LoaderMessageTypeAppStop:
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
loader_do_stop_app(loader);
})
loader_do_stop_app();
break;
case LoaderMessageTypeExit:
exit_requested = true;
@@ -270,30 +281,30 @@ static int32_t loader_main(void* p) {
return 0;
}
static void loader_start(void* p) {
Loader* loader = loader_alloc();
furi_record_create(RECORD_LOADER, loader);
furi_thread_set_priority(loader->thread, FuriThreadPriorityHigh);
// region AppManifest
static void loader_start(void* parameter) {
UNUSED(parameter);
furi_check(loader == NULL);
loader = loader_alloc();
furi_thread_set_priority(loader->thread, FuriThreadPriorityNormal);
furi_thread_start(loader->thread);
}
static void loader_stop() {
furi_check(loader != NULL);
LoaderMessage message = {
.api_lock = NULL,
.type = LoaderMessageTypeExit
};
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
// Send stop signal to thread and wait for thread to finish
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
furi_thread_join(loader->thread);
furi_thread_join(loader->thread);
furi_thread_free(loader->thread);
loader->thread = NULL;
loader_free(loader);
})
furi_record_destroy(RECORD_LOADER);
loader_free();
loader = NULL;
}
const AppManifest loader_app = {
@@ -303,6 +314,7 @@ const AppManifest loader_app = {
.type = AppTypeService,
.on_start = &loader_start,
.on_stop = &loader_stop,
.on_show = NULL,
.stack_size = AppStackSizeNormal
.on_show = NULL
};
// endregion
@@ -8,8 +8,6 @@
extern "C" {
#endif
#define RECORD_LOADER "loader"
typedef struct Loader Loader;
typedef enum {
@@ -36,7 +34,7 @@ typedef struct {
* @param[out] error_message detailed error message, can be NULL
* @return LoaderStatus
*/
LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args, FuriString* error_message);
LoaderStatus loader_start_app(const char* id, const char* args, FuriString* error_message);
/**
* @brief Close any running app, then start new one. Non-blocking.
@@ -44,11 +42,13 @@ LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args,
* @param[in] id application name or id
* @param[in] args application arguments
*/
void loader_start_app_nonblocking(Loader* loader, const char* id, const char* args);
void loader_start_app_nonblocking(const char* id, const char* args);
void loader_stop_app(Loader* loader);
void loader_stop_app();
const AppManifest* _Nullable loader_get_current_app(Loader* loader);
bool loader_is_app_running();
const AppManifest* _Nullable loader_get_current_app();
/**
* @brief Start application with GUI error message
* @param[in] instance loader instance
@@ -56,20 +56,20 @@ const AppManifest* _Nullable loader_get_current_app(Loader* loader);
* @param[in] args application arguments
* @return LoaderStatus
*/
//LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
//LoaderStatus loader_start_with_gui_error(const char* name, const char* args);
/**
* @brief Show loader menu
* @param[in] instance loader instance
*/
void loader_show_menu(Loader* instance);
void loader_show_menu();
/**
* @brief Get loader pubsub
* @param[in] instance loader instance
* @return FuriPubSub*
*/
FuriPubSub* loader_get_pubsub(Loader* instance);
FuriPubSub* loader_get_pubsub();
#ifdef __cplusplus
}
@@ -1,11 +1,13 @@
#pragma once
#include "api_lock.h"
#include "app_manifest.h"
#include "apps/services/gui/view_port.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "loader.h"
#include "message_queue.h"
#include "pubsub.h"
#include "thread.h"
#include "apps/services/gui/view_port.h"
typedef struct {
char* args;
@@ -18,6 +20,7 @@ struct Loader {
FuriPubSub* pubsub;
FuriMessageQueue* queue;
LoaderAppData app_data;
SemaphoreHandle_t mutex;
};
typedef enum {
@@ -38,6 +38,5 @@ AppManifest system_info_app = {
.type = AppTypeSystem,
.on_start = NULL,
.on_stop = NULL,
.on_show = app_show,
.stack_size = AppStackSizeNormal
.on_show = app_show
};
+7 -2
View File
@@ -5,7 +5,12 @@
#define TAG "hardware"
Devices nb_devices_create(Config _Nonnull* config) {
Hardware nb_hardware_init(const HardwareConfig _Nonnull* config) {
if (config->bootstrap != NULL) {
ESP_LOGI(TAG, "Bootstrapping");
config->bootstrap();
}
furi_check(config->display_driver != NULL, "no display driver configured");
DisplayDriver display_driver = config->display_driver();
ESP_LOGI(TAG, "display with driver %s", display_driver.name);
@@ -21,7 +26,7 @@ Devices nb_devices_create(Config _Nonnull* config) {
touch = NULL;
}
return (Devices) {
return (Hardware) {
.display = display,
.touch = touch
};
+1 -1
View File
@@ -10,7 +10,7 @@ extern "C" {
typedef struct {
DisplayDevice* _Nonnull display;
TouchDevice* _Nullable touch;
} Devices;
} Hardware;
#ifdef __cplusplus
}
+1 -1
View File
@@ -6,7 +6,7 @@
extern "C" {
#endif
Devices nb_devices_create(Config _Nonnull* config);
Hardware nb_hardware_init(const HardwareConfig _Nonnull* config);
#ifdef __cplusplus
}
+1
View File
@@ -13,6 +13,7 @@ typedef struct {
uint16_t vertical_resolution;
uint16_t draw_buffer_height;
uint16_t bits_per_pixel;
bool double_buffering;
bool mirror_x;
bool mirror_y;
bool swap_xy;
+2 -2
View File
@@ -5,7 +5,7 @@
#define TAG "lvgl"
Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
Lvgl nb_graphics_init(Hardware _Nonnull* hardware) {
const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = 4,
.task_stack = 4096,
@@ -23,7 +23,7 @@ Lvgl nb_graphics_init(Devices _Nonnull* hardware) {
.io_handle = display->io_handle,
.panel_handle = display->display_handle,
.buffer_size = display->horizontal_resolution * display->draw_buffer_height * (display->bits_per_pixel / 8),
.double_buffer = 0,
.double_buffer = display->double_buffering,
.hres = display->horizontal_resolution,
.vres = display->vertical_resolution,
.monochrome = display->monochrome,
+1 -1
View File
@@ -7,7 +7,7 @@
extern "C" {
#endif
Lvgl nb_graphics_init(Devices _Nonnull* hardware);
Lvgl nb_graphics_init(Hardware _Nonnull* hardware);
#ifdef __cplusplus
}
+5 -3
View File
@@ -4,9 +4,11 @@
#include "furi.h"
#include "graphics_i.h"
#include "partitions.h"
#include "apps/services/gui/gui.h"
#define TAG "nanobake"
Gui* gui_alloc();
// System services
extern const AppManifest gui_app;
extern const AppManifest loader_app;
@@ -50,12 +52,12 @@ static void start_desktop() {
FURI_LOG_I(TAG, "Startup complete");
}
__attribute__((unused)) extern void nanobake_start(Config* _Nonnull config) {
__attribute__((unused)) extern void nanobake_start(const Config* _Nonnull config) {
furi_init();
nb_partitions_init();
Devices hardware = nb_devices_create(config);
Hardware hardware = nb_hardware_init(config->hardware);
/*NbLvgl lvgl =*/nb_graphics_init(&hardware);
register_apps(config);
+8 -1
View File
@@ -10,20 +10,27 @@ extern "C" {
// Forward declarations
typedef void* FuriThreadId;
typedef void (*Bootstrap)();
typedef TouchDriver (*CreateTouchDriver)();
typedef DisplayDriver (*CreateDisplayDriver)();
typedef struct {
// Optional bootstrapping method
const Bootstrap _Nullable bootstrap;
// Required driver for display
const CreateDisplayDriver _Nonnull display_driver;
// Optional driver for touch input
const CreateTouchDriver _Nullable touch_driver;
} HardwareConfig;
typedef struct {
const HardwareConfig* hardware;
// List of user applications
const size_t apps_count;
const AppManifest* const apps[];
} Config;
__attribute__((unused)) extern void nanobake_start(Config _Nonnull* config);
__attribute__((unused)) extern void nanobake_start(const Config _Nonnull* config);
FuriThreadId nanobake_get_app_thread_id(size_t index);
size_t nanobake_get_app_thread_count();