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:
committed by
GitHub
parent
eed990217f
commit
8336316133
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
DisplayDevice* _Nonnull display;
|
||||
TouchDevice* _Nullable touch;
|
||||
} Devices;
|
||||
} Hardware;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Devices nb_devices_create(Config _Nonnull* config);
|
||||
Hardware nb_hardware_init(const HardwareConfig _Nonnull* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Lvgl nb_graphics_init(Devices _Nonnull* hardware);
|
||||
Lvgl nb_graphics_init(Hardware _Nonnull* hardware);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user