implemented gui and view_port
using flipper source (adapted) disabled key input for now disabled non-fullscreen drawing for now
This commit is contained in:
parent
48d875a944
commit
f0cfd3c34d
@ -9,7 +9,7 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
static const char* TAG = "2432s024_ili9341";
|
#define TAG "2432s024_ili9341"
|
||||||
|
|
||||||
static SemaphoreHandle_t refresh_finish = NULL;
|
static SemaphoreHandle_t refresh_finish = NULL;
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#define CST816_I2C_PORT (0)
|
#define CST816_I2C_PORT (0)
|
||||||
|
|
||||||
const char* TAG = "2432s024_cst816";
|
#define TAG "2432s024_cst816"
|
||||||
|
|
||||||
static bool prv_create_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) {
|
static bool prv_create_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) {
|
||||||
ESP_LOGI(TAG, "creating touch");
|
ESP_LOGI(TAG, "creating touch");
|
||||||
|
|||||||
@ -3,83 +3,231 @@
|
|||||||
#include "record.h"
|
#include "record.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
|
||||||
static NbScreenId screen_counter = 0;
|
#define TAG "Gui"
|
||||||
|
|
||||||
NbGuiHandle gui_alloc() {
|
// Forward declarations from gui_draw.c
|
||||||
struct NbGui* gui = malloc(sizeof(struct NbGui));
|
bool gui_redraw_fs(NbGui* gui);
|
||||||
ScreenDict_init(gui->screens);
|
void gui_redraw(NbGui* gui);
|
||||||
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
|
||||||
return gui;
|
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)) {
|
||||||
|
return view_port;
|
||||||
|
}
|
||||||
|
ViewPortArray_previous(it);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_free(NbGuiHandle gui) {
|
size_t gui_active_view_port_count(NbGui* gui, GuiLayer layer) {
|
||||||
ScreenDict_clear(gui->screens);
|
furi_assert(gui);
|
||||||
furi_mutex_free(gui->mutex);
|
furi_check(layer < GuiLayerMAX);
|
||||||
free(gui);
|
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_lock(NbGuiHandle gui) {
|
void gui_update(NbGui* gui) {
|
||||||
|
ESP_LOGI(TAG, "gui_update");
|
||||||
|
furi_assert(gui);
|
||||||
|
furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_lock(NbGui* gui) {
|
||||||
furi_assert(gui);
|
furi_assert(gui);
|
||||||
furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk);
|
furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_unlock(NbGuiHandle gui) {
|
void gui_unlock(NbGui* gui) {
|
||||||
furi_assert(gui);
|
furi_assert(gui);
|
||||||
furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk);
|
furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk);
|
||||||
}
|
}
|
||||||
|
|
||||||
NbScreenId gui_screen_create(NbGuiHandle gui, InitScreen callback) {
|
void gui_add_view_port(NbGui* gui, ViewPort* view_port, GuiLayer layer) {
|
||||||
NbScreenId id = screen_counter++;
|
furi_assert(gui);
|
||||||
NbScreen screen = {
|
furi_assert(view_port);
|
||||||
.id = id,
|
furi_check(layer < GuiLayerMAX);
|
||||||
.parent = NULL,
|
|
||||||
.callback = callback
|
|
||||||
};
|
|
||||||
|
|
||||||
ScreenDict_set_at(gui->screens, id, screen);
|
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);
|
||||||
|
|
||||||
// TODO: notify desktop of change
|
// Request redraw
|
||||||
// TODO: have desktop update views
|
gui_update(gui);
|
||||||
lv_obj_t* parent = lv_scr_act();
|
|
||||||
gui_screen_set_parent(gui, id, parent);
|
|
||||||
|
|
||||||
// TODO: call from desktop
|
|
||||||
screen.callback(gui_screen_get_parent(gui, id), id);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_obj_t* gui_screen_get_parent(NbGuiHandle gui, NbScreenId id) {
|
void gui_remove_view_port(NbGui* gui, ViewPort* view_port) {
|
||||||
NbScreen* screen = ScreenDict_get(gui->screens, id);
|
furi_assert(gui);
|
||||||
furi_check(screen != NULL);
|
furi_assert(view_port);
|
||||||
return screen->parent;
|
|
||||||
|
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_screen_set_parent(NbGuiHandle gui, NbScreenId id, lv_obj_t* parent) {
|
void gui_view_port_send_to_front(NbGui* gui, ViewPort* view_port) {
|
||||||
NbScreen* screen = ScreenDict_get(gui->screens, id);
|
furi_assert(gui);
|
||||||
furi_check(screen != NULL);
|
furi_assert(view_port);
|
||||||
screen->parent = parent;
|
|
||||||
|
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_screen_free(NbGuiHandle gui, NbScreenId id) {
|
void gui_view_port_send_to_back(NbGui* gui, ViewPort* view_port) {
|
||||||
NbScreen* screen = ScreenDict_get(gui->screens, id);
|
furi_assert(gui);
|
||||||
furi_check(screen != NULL);
|
furi_assert(view_port);
|
||||||
|
|
||||||
// TODO: notify? use callback? (done from desktop service)
|
gui_lock(gui);
|
||||||
lv_obj_clean(screen->parent);
|
// 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);
|
||||||
|
|
||||||
ScreenDict_erase(gui->screens, id);
|
// Request redraw
|
||||||
|
gui_update(gui);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t prv_gui_main(void* param) {
|
void gui_set_lockdown(NbGui* gui, bool lockdown) {
|
||||||
UNUSED(param);
|
furi_assert(gui);
|
||||||
|
|
||||||
|
gui_lock(gui);
|
||||||
|
gui->lockdown = lockdown;
|
||||||
|
gui_unlock(gui);
|
||||||
|
|
||||||
|
// Request redraw
|
||||||
|
gui_update(gui);
|
||||||
|
}
|
||||||
|
|
||||||
|
NbGui* gui_alloc() {
|
||||||
|
NbGui* gui = malloc(sizeof(NbGui));
|
||||||
|
gui->thread_id = furi_thread_get_current_id();
|
||||||
|
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
|
gui->lvgl_parent = lv_scr_act();
|
||||||
|
gui->lockdown = false;
|
||||||
|
furi_check(gui->mutex);
|
||||||
|
for(size_t i = 0; i < GuiLayerMAX; i++) {
|
||||||
|
ViewPortArray_init(gui->layers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Input
|
||||||
|
gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||||
|
gui->input_events = furi_record_open(RECORD_INPUT_EVENTS);
|
||||||
|
|
||||||
|
furi_check(gui->input_events);
|
||||||
|
furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui);
|
||||||
|
*/
|
||||||
|
return gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute((__noreturn__)) int32_t prv_gui_main(void* parameter) {
|
||||||
|
UNUSED(parameter);
|
||||||
|
NbGui* gui = gui_alloc();
|
||||||
|
|
||||||
struct NbGui* gui = gui_alloc();
|
|
||||||
furi_record_create(RECORD_GUI, gui);
|
furi_record_create(RECORD_GUI, gui);
|
||||||
printf("gui app init\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
uint32_t flags = furi_thread_flags_wait(
|
||||||
|
GUI_THREAD_FLAG_ALL,
|
||||||
|
FuriFlagWaitAny,
|
||||||
|
FuriWaitForever
|
||||||
|
);
|
||||||
|
// Process and dispatch input
|
||||||
|
if (flags & GUI_THREAD_FLAG_INPUT) {
|
||||||
|
// // Process till queue become empty
|
||||||
|
// InputEvent input_event;
|
||||||
|
// while(furi_message_queue_get(gui->input_queue, &input_event, 0) == FuriStatusOk) {
|
||||||
|
// gui_input(gui, &input_event);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// Process and dispatch draw call
|
||||||
|
if (flags & GUI_THREAD_FLAG_DRAW) {
|
||||||
|
// Clear flags that arrived on input step
|
||||||
|
furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW);
|
||||||
|
gui_redraw(gui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const NbApp gui_app = {
|
const NbApp gui_app = {
|
||||||
.id = "gui",
|
.id = "gui",
|
||||||
.name = "GUI",
|
.name = "GUI",
|
||||||
|
|||||||
@ -1,25 +1,94 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "view_port.h"
|
||||||
|
#include "lvgl.h"
|
||||||
#include "nb_app.h"
|
#include "nb_app.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern const NbApp gui_app;
|
||||||
|
|
||||||
|
/** Canvas Orientation */
|
||||||
|
typedef enum {
|
||||||
|
CanvasOrientationHorizontal,
|
||||||
|
CanvasOrientationHorizontalFlip,
|
||||||
|
CanvasOrientationVertical,
|
||||||
|
CanvasOrientationVerticalFlip,
|
||||||
|
} CanvasOrientation;
|
||||||
|
|
||||||
|
/** 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,
|
||||||
|
CanvasOrientation orientation,
|
||||||
|
void* context);
|
||||||
|
|
||||||
#define RECORD_GUI "gui"
|
#define RECORD_GUI "gui"
|
||||||
|
|
||||||
typedef uint16_t NbScreenId;
|
typedef struct NbGui NbGui;
|
||||||
|
|
||||||
typedef struct NbGui* NbGuiHandle;
|
/** Add view_port to view_port tree
|
||||||
typedef void (*InitScreen)(lv_obj_t*, NbScreenId);
|
*
|
||||||
|
* @remark thread safe
|
||||||
|
*
|
||||||
|
* @param gui Gui instance
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
* @param[in] layer GuiLayer where to place view_port
|
||||||
|
*/
|
||||||
|
void gui_add_view_port(NbGui* gui, ViewPort* view_port, GuiLayer layer);
|
||||||
|
|
||||||
NbScreenId gui_screen_create(NbGuiHandle _Nonnull gui, InitScreen callback);
|
/** Remove view_port from rendering tree
|
||||||
void gui_screen_free(NbGuiHandle _Nonnull gui, NbScreenId id);
|
*
|
||||||
// TODO make internal
|
* @remark thread safe
|
||||||
void gui_screen_set_parent(NbGuiHandle _Nonnull gui, NbScreenId id, lv_obj_t* parent);
|
*
|
||||||
lv_obj_t* gui_screen_get_parent(NbGuiHandle _Nonnull gui, NbScreenId id);
|
* @param gui Gui instance
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
*/
|
||||||
|
void gui_remove_view_port(NbGui* gui, ViewPort* view_port);
|
||||||
|
|
||||||
extern const NbApp gui_app;
|
/** 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(NbGui* 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(NbGui* gui, ViewPort* view_port);
|
||||||
|
|
||||||
|
/** Set lockdown mode
|
||||||
|
*
|
||||||
|
* When lockdown mode is enabled, only GuiLayerDesktop is shown.
|
||||||
|
* This feature prevents services from showing sensitive information when flipper is locked.
|
||||||
|
*
|
||||||
|
* @param gui Gui instance
|
||||||
|
* @param lockdown bool, true if enabled
|
||||||
|
*/
|
||||||
|
void gui_set_lockdown(NbGui* gui, bool lockdown);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
206
components/nanobake/src/applications/services/gui/gui_draw.c
Normal file
206
components/nanobake/src/applications/services/gui/gui_draw.c
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
#include "gui_i.h"
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
static void gui_redraw_status_bar(NbGui* gui, bool need_attention) {
|
||||||
|
// ViewPortArray_it_t it;
|
||||||
|
// uint8_t left_used = 0;
|
||||||
|
// uint8_t right_used = 0;
|
||||||
|
// uint8_t width;
|
||||||
|
//
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->lvgl_parent, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT);
|
||||||
|
//
|
||||||
|
// /* for support black theme - paint white area and
|
||||||
|
// * draw icon with transparent white color
|
||||||
|
// */
|
||||||
|
// canvas_set_color(gui->canvas, ColorWhite);
|
||||||
|
// canvas_draw_box(gui->canvas, 1, 1, 9, 7);
|
||||||
|
// canvas_draw_box(gui->canvas, 7, 3, 58, 6);
|
||||||
|
// canvas_draw_box(gui->canvas, 61, 1, 32, 7);
|
||||||
|
// canvas_draw_box(gui->canvas, 89, 3, 38, 6);
|
||||||
|
// canvas_set_color(gui->canvas, ColorBlack);
|
||||||
|
// canvas_set_bitmap_mode(gui->canvas, 1);
|
||||||
|
// canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11);
|
||||||
|
// canvas_set_bitmap_mode(gui->canvas, 0);
|
||||||
|
//
|
||||||
|
// // Right side
|
||||||
|
// uint8_t x = GUI_DISPLAY_WIDTH - 1;
|
||||||
|
// ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]);
|
||||||
|
// while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) {
|
||||||
|
// ViewPort* view_port = *ViewPortArray_ref(it);
|
||||||
|
// if(view_port_is_enabled(view_port)) {
|
||||||
|
// width = view_port_get_width(view_port);
|
||||||
|
// if(!width) width = 8;
|
||||||
|
// // Recalculate next position
|
||||||
|
// right_used += (width + 2);
|
||||||
|
// x -= (width + 2);
|
||||||
|
// // Prepare work area background
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas,
|
||||||
|
// x - 1,
|
||||||
|
// GUI_STATUS_BAR_Y + 1,
|
||||||
|
// width + 2,
|
||||||
|
// GUI_STATUS_BAR_WORKAREA_HEIGHT + 2);
|
||||||
|
// canvas_set_color(gui->canvas, ColorWhite);
|
||||||
|
// canvas_draw_box(
|
||||||
|
// gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas));
|
||||||
|
// canvas_set_color(gui->canvas, ColorBlack);
|
||||||
|
// // ViewPort draw
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT);
|
||||||
|
// view_port_draw(view_port, gui->canvas);
|
||||||
|
// }
|
||||||
|
// ViewPortArray_next(it);
|
||||||
|
// }
|
||||||
|
// // Draw frame around icons on the right
|
||||||
|
// if(right_used) {
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas,
|
||||||
|
// GUI_DISPLAY_WIDTH - 3 - right_used,
|
||||||
|
// GUI_STATUS_BAR_Y,
|
||||||
|
// right_used + 3,
|
||||||
|
// GUI_STATUS_BAR_HEIGHT);
|
||||||
|
// canvas_set_color(gui->canvas, ColorBlack);
|
||||||
|
// canvas_draw_rframe(
|
||||||
|
// gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1);
|
||||||
|
// canvas_draw_line(
|
||||||
|
// gui->canvas,
|
||||||
|
// canvas_width(gui->canvas) - 2,
|
||||||
|
// 1,
|
||||||
|
// canvas_width(gui->canvas) - 2,
|
||||||
|
// canvas_height(gui->canvas) - 2);
|
||||||
|
// canvas_draw_line(
|
||||||
|
// gui->canvas,
|
||||||
|
// 1,
|
||||||
|
// canvas_height(gui->canvas) - 2,
|
||||||
|
// canvas_width(gui->canvas) - 2,
|
||||||
|
// canvas_height(gui->canvas) - 2);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Left side
|
||||||
|
// x = 2;
|
||||||
|
// ViewPortArray_it(it, gui->layers[GuiLayerStatusBarLeft]);
|
||||||
|
// while(!ViewPortArray_end_p(it) && (right_used + left_used) < GUI_STATUS_BAR_WIDTH) {
|
||||||
|
// ViewPort* view_port = *ViewPortArray_ref(it);
|
||||||
|
// if(view_port_is_enabled(view_port)) {
|
||||||
|
// width = view_port_get_width(view_port);
|
||||||
|
// if(!width) width = 8;
|
||||||
|
// // Prepare work area background
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas,
|
||||||
|
// x - 1,
|
||||||
|
// GUI_STATUS_BAR_Y + 1,
|
||||||
|
// width + 2,
|
||||||
|
// GUI_STATUS_BAR_WORKAREA_HEIGHT + 2);
|
||||||
|
// canvas_set_color(gui->canvas, ColorWhite);
|
||||||
|
// canvas_draw_box(
|
||||||
|
// gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas));
|
||||||
|
// canvas_set_color(gui->canvas, ColorBlack);
|
||||||
|
// // ViewPort draw
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT);
|
||||||
|
// view_port_draw(view_port, gui->canvas);
|
||||||
|
// // Recalculate next position
|
||||||
|
// left_used += (width + 2);
|
||||||
|
// x += (width + 2);
|
||||||
|
// }
|
||||||
|
// ViewPortArray_next(it);
|
||||||
|
// }
|
||||||
|
// // Extra notification
|
||||||
|
// if(need_attention) {
|
||||||
|
// width = icon_get_width(&I_Hidden_window_9x8);
|
||||||
|
// // Prepare work area background
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas,
|
||||||
|
// x - 1,
|
||||||
|
// GUI_STATUS_BAR_Y + 1,
|
||||||
|
// width + 2,
|
||||||
|
// GUI_STATUS_BAR_WORKAREA_HEIGHT + 2);
|
||||||
|
// canvas_set_color(gui->canvas, ColorWhite);
|
||||||
|
// canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas));
|
||||||
|
// canvas_set_color(gui->canvas, ColorBlack);
|
||||||
|
// // Draw Icon
|
||||||
|
// canvas_frame_set(
|
||||||
|
// gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT);
|
||||||
|
// canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8);
|
||||||
|
// // Recalculate next position
|
||||||
|
// left_used += (width + 2);
|
||||||
|
// x += (width + 2);
|
||||||
|
// }
|
||||||
|
// // Draw frame around icons on the left
|
||||||
|
// if(left_used) {
|
||||||
|
// canvas_frame_set(gui->canvas, 0, 0, left_used + 3, GUI_STATUS_BAR_HEIGHT);
|
||||||
|
// canvas_draw_rframe(
|
||||||
|
// gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1);
|
||||||
|
// canvas_draw_line(
|
||||||
|
// gui->canvas,
|
||||||
|
// canvas_width(gui->canvas) - 2,
|
||||||
|
// 1,
|
||||||
|
// canvas_width(gui->canvas) - 2,
|
||||||
|
// canvas_height(gui->canvas) - 2);
|
||||||
|
// canvas_draw_line(
|
||||||
|
// gui->canvas,
|
||||||
|
// 1,
|
||||||
|
// canvas_height(gui->canvas) - 2,
|
||||||
|
// canvas_width(gui->canvas) - 2,
|
||||||
|
// canvas_height(gui->canvas) - 2);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool gui_redraw_window(NbGui* gui) {
|
||||||
|
// canvas_frame_set(gui->lvgl_parent, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT);
|
||||||
|
// ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
|
||||||
|
// if(view_port) {
|
||||||
|
// view_port_draw(view_port, gui->lvgl_parent);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool gui_redraw_desktop(NbGui* gui) {
|
||||||
|
// canvas_frame_set(gui->lvgl_parent, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
|
||||||
|
// ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
|
||||||
|
// if(view_port) {
|
||||||
|
// view_port_draw(view_port, gui->lvgl_parent);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gui_redraw_fs(NbGui* gui) {
|
||||||
|
ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
|
||||||
|
if (view_port) {
|
||||||
|
view_port_draw(view_port, gui->lvgl_parent);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_redraw(NbGui* gui) {
|
||||||
|
furi_assert(gui);
|
||||||
|
gui_lock(gui);
|
||||||
|
|
||||||
|
lv_obj_clean(gui->lvgl_parent);
|
||||||
|
|
||||||
|
if(gui->lockdown) {
|
||||||
|
ESP_LOGI("gui", "gui_redraw with lockdown");
|
||||||
|
gui_redraw_desktop(gui);
|
||||||
|
bool need_attention =
|
||||||
|
(gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 ||
|
||||||
|
gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0);
|
||||||
|
gui_redraw_status_bar(gui, need_attention);
|
||||||
|
} else {
|
||||||
|
gui_redraw_desktop(gui);
|
||||||
|
ESP_LOGI("gui", "gui_redraw");
|
||||||
|
if (!gui_redraw_fs(gui)) {
|
||||||
|
if (!gui_redraw_window(gui)) {
|
||||||
|
gui_redraw_desktop(gui);
|
||||||
|
}
|
||||||
|
gui_redraw_status_bar(gui, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gui_unlock(gui);
|
||||||
|
}
|
||||||
@ -1,20 +1,106 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
|
||||||
|
#include <m-array.h>
|
||||||
|
#include <m-algo.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "view_port.h"
|
||||||
|
#include "view_port_i.h"
|
||||||
|
#include "message_queue.h"
|
||||||
|
#include "pubsub.h"
|
||||||
#include "mutex.h"
|
#include "mutex.h"
|
||||||
#include "m-dict.h"
|
|
||||||
#include "m-core.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)
|
||||||
|
|
||||||
|
#define GUI_THREAD_FLAG_DRAW (1 << 0)
|
||||||
|
#define GUI_THREAD_FLAG_INPUT (1 << 1)
|
||||||
|
#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT)
|
||||||
|
|
||||||
|
ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
NbScreenId id;
|
GuiCanvasCommitCallback callback;
|
||||||
lv_obj_t* parent;
|
void* context;
|
||||||
InitScreen _Nonnull callback;
|
} CanvasCallbackPair;
|
||||||
} NbScreen;
|
|
||||||
|
|
||||||
DICT_DEF2(ScreenDict, NbScreenId, M_BASIC_OPLIST, NbScreen, M_POD_OPLIST)
|
|
||||||
|
|
||||||
|
/** Gui structure */
|
||||||
struct NbGui {
|
struct NbGui {
|
||||||
// TODO: use mutex
|
// Thread and lock
|
||||||
|
FuriThreadId thread_id;
|
||||||
FuriMutex* mutex;
|
FuriMutex* mutex;
|
||||||
ScreenDict_t screens;
|
|
||||||
|
// Layers and Canvas
|
||||||
|
bool lockdown;
|
||||||
|
ViewPortArray_t layers[GuiLayerMAX];
|
||||||
|
lv_obj_t* lvgl_parent;
|
||||||
|
|
||||||
|
// Input
|
||||||
|
/*
|
||||||
|
FuriMessageQueue* input_queue;
|
||||||
|
FuriPubSub* input_events;
|
||||||
|
uint8_t ongoing_input;
|
||||||
|
ViewPort* ongoing_input_view_port;
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 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(NbGui* gui);
|
||||||
|
|
||||||
|
///** Input event callback
|
||||||
|
// *
|
||||||
|
// * Used to receive input from input service or to inject new input events
|
||||||
|
// *
|
||||||
|
// * @param[in] value The value pointer (InputEvent*)
|
||||||
|
// * @param ctx The context (Gui instance)
|
||||||
|
// */
|
||||||
|
//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(NbGui* gui, GuiLayer layer);
|
||||||
|
|
||||||
|
/** Lock GUI
|
||||||
|
*
|
||||||
|
* @param gui The Gui instance
|
||||||
|
*/
|
||||||
|
void gui_lock(NbGui* gui);
|
||||||
|
|
||||||
|
/** Unlock GUI
|
||||||
|
*
|
||||||
|
* @param gui The Gui instance
|
||||||
|
*/
|
||||||
|
void gui_unlock(NbGui* gui);
|
||||||
|
|||||||
@ -0,0 +1,81 @@
|
|||||||
|
#include "gui_i.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
void gui_input_events_callback(const void* value, void* ctx) {
|
||||||
|
furi_assert(value);
|
||||||
|
furi_assert(ctx);
|
||||||
|
|
||||||
|
Gui* gui = ctx;
|
||||||
|
|
||||||
|
furi_message_queue_put(gui->input_queue, value, FuriWaitForever);
|
||||||
|
furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gui_input(Gui* gui, InputEvent* input_event) {
|
||||||
|
furi_assert(gui);
|
||||||
|
furi_assert(input_event);
|
||||||
|
|
||||||
|
// Check input complementarity
|
||||||
|
uint8_t key_bit = (1 << input_event->key);
|
||||||
|
if(input_event->type == InputTypeRelease) {
|
||||||
|
gui->ongoing_input &= ~key_bit;
|
||||||
|
} else if(input_event->type == InputTypePress) {
|
||||||
|
gui->ongoing_input |= key_bit;
|
||||||
|
} else if(!(gui->ongoing_input & key_bit)) {
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"non-complementary input, discarding key: %s type: %s, sequence: %p",
|
||||||
|
input_get_key_name(input_event->key),
|
||||||
|
input_get_type_name(input_event->type),
|
||||||
|
(void*)input_event->sequence);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui_lock(gui);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(gui->direct_draw && !gui->ongoing_input_view_port) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewPort* view_port = NULL;
|
||||||
|
|
||||||
|
if(gui->lockdown) {
|
||||||
|
view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
|
||||||
|
} else {
|
||||||
|
view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
|
||||||
|
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
|
||||||
|
if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) {
|
||||||
|
gui->ongoing_input_view_port = view_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(view_port && view_port == gui->ongoing_input_view_port) {
|
||||||
|
view_port_input(view_port, input_event);
|
||||||
|
} else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
|
||||||
|
gui->ongoing_input_view_port,
|
||||||
|
view_port,
|
||||||
|
input_get_key_name(input_event->key),
|
||||||
|
input_get_type_name(input_event->type),
|
||||||
|
(void*)input_event->sequence);
|
||||||
|
view_port_input(gui->ongoing_input_view_port, input_event);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
|
||||||
|
gui->ongoing_input_view_port,
|
||||||
|
view_port,
|
||||||
|
input_get_key_name(input_event->key),
|
||||||
|
input_get_type_name(input_event->type),
|
||||||
|
(void*)input_event->sequence);
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
gui_unlock(gui);
|
||||||
|
}
|
||||||
|
*/
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
#include "view_port_i.h"
|
||||||
|
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gui_i.h"
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
#define TAG "viewport"
|
||||||
|
|
||||||
|
_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count");
|
||||||
|
_Static_assert(
|
||||||
|
(ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 &&
|
||||||
|
ViewPortOrientationVertical == 2 && ViewPortOrientationVerticalFlip == 3),
|
||||||
|
"Incorrect ViewPortOrientation order");
|
||||||
|
|
||||||
|
ViewPort* view_port_alloc() {
|
||||||
|
ViewPort* view_port = malloc(sizeof(ViewPort));
|
||||||
|
view_port->gui = NULL;
|
||||||
|
view_port->is_enabled = true;
|
||||||
|
view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||||
|
return view_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_free(ViewPort* view_port) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
furi_check(view_port->gui == NULL);
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
furi_mutex_free(view_port->mutex);
|
||||||
|
free(view_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_enabled_set(ViewPort* view_port, bool enabled) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool view_port_is_enabled(const ViewPort* view_port) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
bool is_enabled = view_port->is_enabled;
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
return is_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
view_port->draw_callback = callback;
|
||||||
|
view_port->draw_callback_context = context;
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_update(ViewPort* view_port) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
|
||||||
|
// We are not going to lockup system, but will notify you instead
|
||||||
|
// Make sure that you don't call viewport methods inside another mutex, especially one that is used in draw call
|
||||||
|
if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {
|
||||||
|
ESP_LOGW(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
|
||||||
|
furi_mutex_release(view_port->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_gui_set(ViewPort* view_port, NbGui* gui) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
view_port->gui = gui;
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_draw(ViewPort* view_port, lv_obj_t* parent) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_assert(parent);
|
||||||
|
|
||||||
|
// We are not going to lockup system, but will notify you instead
|
||||||
|
// Make sure that you don't call viewport methods inside another mutex, especially one that is used in draw call
|
||||||
|
if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {
|
||||||
|
ESP_LOGW(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_check(view_port->gui);
|
||||||
|
|
||||||
|
if (view_port->draw_callback) {
|
||||||
|
lv_obj_clean(parent);
|
||||||
|
view_port->draw_callback(parent, view_port->draw_callback_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_mutex_release(view_port->mutex);
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
typedef struct ViewPort ViewPort;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ViewPortOrientationHorizontal,
|
||||||
|
ViewPortOrientationHorizontalFlip,
|
||||||
|
ViewPortOrientationVertical,
|
||||||
|
ViewPortOrientationVerticalFlip,
|
||||||
|
ViewPortOrientationMAX, /**< Special value, don't use it */
|
||||||
|
} ViewPortOrientation;
|
||||||
|
|
||||||
|
/** ViewPort Draw callback
|
||||||
|
* @warning called from GUI thread
|
||||||
|
*/
|
||||||
|
typedef void (*ViewPortDrawCallback)(lv_obj_t* parent, void* context);
|
||||||
|
|
||||||
|
/** ViewPort allocator
|
||||||
|
*
|
||||||
|
* always returns view_port or stops system if not enough memory.
|
||||||
|
*
|
||||||
|
* @return ViewPort instance
|
||||||
|
*/
|
||||||
|
ViewPort* view_port_alloc();
|
||||||
|
|
||||||
|
/** ViewPort deallocator
|
||||||
|
*
|
||||||
|
* Ensure that view_port was unregistered in GUI system before use.
|
||||||
|
*
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
*/
|
||||||
|
void view_port_free(ViewPort* view_port);
|
||||||
|
|
||||||
|
/** Enable or disable view_port rendering.
|
||||||
|
*
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
* @param enabled Indicates if enabled
|
||||||
|
* @warning automatically dispatches update event
|
||||||
|
*/
|
||||||
|
void view_port_enabled_set(ViewPort* view_port, bool enabled);
|
||||||
|
bool view_port_is_enabled(const ViewPort* view_port);
|
||||||
|
|
||||||
|
/** ViewPort event callbacks
|
||||||
|
*
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
* @param callback appropriate callback function
|
||||||
|
* @param context context to pass to callback
|
||||||
|
*/
|
||||||
|
void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context);
|
||||||
|
/** Emit update signal to GUI system.
|
||||||
|
*
|
||||||
|
* Rendering will happen later after GUI system process signal.
|
||||||
|
*
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
*/
|
||||||
|
void view_port_update(ViewPort* view_port);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gui_i.h"
|
||||||
|
#include "view_port.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
struct ViewPort {
|
||||||
|
NbGui* gui;
|
||||||
|
FuriMutex* mutex;
|
||||||
|
bool is_enabled;
|
||||||
|
|
||||||
|
ViewPortDrawCallback draw_callback;
|
||||||
|
void* draw_callback_context;
|
||||||
|
|
||||||
|
// ViewPortInputCallback input_callback;
|
||||||
|
// void* input_callback_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Set GUI reference.
|
||||||
|
*
|
||||||
|
* To be used by GUI, called upon view_port tree insert
|
||||||
|
*
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
* @param gui gui instance pointer
|
||||||
|
*/
|
||||||
|
void view_port_gui_set(ViewPort* view_port, NbGui* gui);
|
||||||
|
|
||||||
|
/** Process draw call. Calls draw callback.
|
||||||
|
*
|
||||||
|
* To be used by GUI, called on tree redraw.
|
||||||
|
*
|
||||||
|
* @param view_port ViewPort instance
|
||||||
|
* @param canvas canvas to draw at
|
||||||
|
*/
|
||||||
|
void view_port_draw(ViewPort* view_port, lv_obj_t* parent);
|
||||||
|
|
||||||
|
/** Process input. Calls input callback.
|
||||||
|
// *
|
||||||
|
// * To be used by GUI, called on input dispatch.
|
||||||
|
// *
|
||||||
|
// * @param view_port ViewPort instance
|
||||||
|
// * @param event pointer to input event
|
||||||
|
// */
|
||||||
|
//void view_port_input(ViewPort* view_port, InputEvent* event);
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
#include "view_port_input.h"
|
||||||
|
/*
|
||||||
|
_Static_assert(InputKeyMAX == 6, "Incorrect InputKey count");
|
||||||
|
_Static_assert(
|
||||||
|
(InputKeyUp == 0 && InputKeyDown == 1 && InputKeyRight == 2 && InputKeyLeft == 3 &&
|
||||||
|
InputKeyOk == 4 && InputKeyBack == 5),
|
||||||
|
"Incorrect InputKey order");
|
||||||
|
*/
|
||||||
|
/** InputKey directional keys mappings for different screen orientations
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
static const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMAX] = {
|
||||||
|
{InputKeyUp,
|
||||||
|
InputKeyDown,
|
||||||
|
InputKeyRight,
|
||||||
|
InputKeyLeft,
|
||||||
|
InputKeyOk,
|
||||||
|
InputKeyBack}, //ViewPortOrientationHorizontal
|
||||||
|
{InputKeyDown,
|
||||||
|
InputKeyUp,
|
||||||
|
InputKeyLeft,
|
||||||
|
InputKeyRight,
|
||||||
|
InputKeyOk,
|
||||||
|
InputKeyBack}, //ViewPortOrientationHorizontalFlip
|
||||||
|
{InputKeyRight,
|
||||||
|
InputKeyLeft,
|
||||||
|
InputKeyDown,
|
||||||
|
InputKeyUp,
|
||||||
|
InputKeyOk,
|
||||||
|
InputKeyBack}, //ViewPortOrientationVertical
|
||||||
|
{InputKeyLeft,
|
||||||
|
InputKeyRight,
|
||||||
|
InputKeyUp,
|
||||||
|
InputKeyDown,
|
||||||
|
InputKeyOk,
|
||||||
|
InputKeyBack}, //ViewPortOrientationVerticalFlip
|
||||||
|
};
|
||||||
|
|
||||||
|
static const InputKey view_port_left_hand_input_mapping[InputKeyMAX] =
|
||||||
|
{InputKeyDown, InputKeyUp, InputKeyLeft, InputKeyRight, InputKeyOk, InputKeyBack};
|
||||||
|
|
||||||
|
static const CanvasOrientation view_port_orientation_mapping[ViewPortOrientationMAX] = {
|
||||||
|
[ViewPortOrientationHorizontal] = CanvasOrientationHorizontal,
|
||||||
|
[ViewPortOrientationHorizontalFlip] = CanvasOrientationHorizontalFlip,
|
||||||
|
[ViewPortOrientationVertical] = CanvasOrientationVertical,
|
||||||
|
[ViewPortOrientationVerticalFlip] = CanvasOrientationVerticalFlip,
|
||||||
|
};
|
||||||
|
|
||||||
|
//// Remaps directional pad buttons on Flipper based on ViewPort orientation
|
||||||
|
static void view_port_map_input(InputEvent* event, ViewPortOrientation orientation) {
|
||||||
|
furi_assert(orientation < ViewPortOrientationMAX && event->key < InputKeyMAX);
|
||||||
|
|
||||||
|
if(event->sequence_source != INPUT_SEQUENCE_SOURCE_HARDWARE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(orientation == ViewPortOrientationHorizontal ||
|
||||||
|
orientation == ViewPortOrientationHorizontalFlip) {
|
||||||
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) {
|
||||||
|
event->key = view_port_left_hand_input_mapping[event->key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event->key = view_port_input_mapping[orientation][event->key];
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_input_callback_set(
|
||||||
|
ViewPort* view_port,
|
||||||
|
ViewPortInputCallback callback,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
view_port->input_callback = callback;
|
||||||
|
view_port->input_callback_context = context;
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_port_input(ViewPort* view_port, InputEvent* event) {
|
||||||
|
furi_assert(view_port);
|
||||||
|
furi_assert(event);
|
||||||
|
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
furi_check(view_port->gui);
|
||||||
|
|
||||||
|
if(view_port->input_callback) {
|
||||||
|
ViewPortOrientation orientation = view_port_get_orientation(view_port);
|
||||||
|
view_port_map_input(event, orientation);
|
||||||
|
view_port->input_callback(event, view_port->input_callback_context);
|
||||||
|
}
|
||||||
|
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
*/
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** ViewPort Input callback
|
||||||
|
* @warning called from GUI thread
|
||||||
|
*/
|
||||||
|
//typedef void (*ViewPortInputCallback)(InputEvent* event, void* context);
|
||||||
|
|
||||||
|
//void view_port_input_callback_set(
|
||||||
|
// ViewPort* view_port,
|
||||||
|
// ViewPortInputCallback callback,
|
||||||
|
// void* context);
|
||||||
|
//
|
||||||
@ -12,7 +12,8 @@
|
|||||||
|
|
||||||
M_LIST_DEF(thread_ids, FuriThreadId);
|
M_LIST_DEF(thread_ids, FuriThreadId);
|
||||||
|
|
||||||
static const char* TAG = "nanobake";
|
#define TAG "nanobake"
|
||||||
|
|
||||||
thread_ids_t prv_thread_ids;
|
thread_ids_t prv_thread_ids;
|
||||||
|
|
||||||
static void prv_furi_init() {
|
static void prv_furi_init() {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
|
||||||
static const char* TAG = "nb_hardware";
|
#define TAG "nb_hardware"
|
||||||
|
|
||||||
NbHardware nb_hardware_create(NbConfig _Nonnull* config) {
|
NbHardware nb_hardware_create(NbConfig _Nonnull* config) {
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
|
||||||
static const char* TAG = "nb_lvgl";
|
#define TAG "nb_lvgl"
|
||||||
|
|
||||||
NbLvgl nb_lvgl_init(NbHardware _Nonnull* hardware) {
|
NbLvgl nb_lvgl_init(NbHardware _Nonnull* hardware) {
|
||||||
const lvgl_port_cfg_t lvgl_cfg = {
|
const lvgl_port_cfg_t lvgl_cfg = {
|
||||||
|
|||||||
@ -9,21 +9,24 @@
|
|||||||
|
|
||||||
static const char* TAG = "app_helloworld";
|
static const char* TAG = "app_helloworld";
|
||||||
|
|
||||||
|
ViewPort* view_port = NULL;
|
||||||
|
|
||||||
static void prv_on_button_click(lv_event_t _Nonnull* event) {
|
static void prv_on_button_click(lv_event_t _Nonnull* event) {
|
||||||
ESP_LOGI(TAG, "button clicked");
|
ESP_LOGI(TAG, "button clicked");
|
||||||
// Open Gui record
|
|
||||||
struct NbGui* gui = furi_record_open(RECORD_GUI);
|
|
||||||
|
|
||||||
// Free this screen
|
// TODO: make macro for record 'transactions'
|
||||||
NbScreenId screen_id = (NbScreenId)event->user_data;
|
NbGui* gui = furi_record_open(RECORD_GUI);
|
||||||
gui_screen_free(gui, screen_id);
|
gui_remove_view_port(gui, view_port);
|
||||||
|
|
||||||
|
view_port_free(view_port);
|
||||||
|
view_port = NULL;
|
||||||
|
|
||||||
// Close Gui record
|
// Close Gui record
|
||||||
furi_record_close(RECORD_GUI);
|
furi_record_close(RECORD_GUI);
|
||||||
gui = NULL;
|
gui = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prv_hello_world_lvgl(lv_obj_t* parent, NbScreenId screen_id) {
|
static void prv_hello_world_lvgl(lv_obj_t* parent, void* context) {
|
||||||
lvgl_port_lock(0);
|
lvgl_port_lock(0);
|
||||||
|
|
||||||
lv_obj_t* label = lv_label_create(parent);
|
lv_obj_t* label = lv_label_create(parent);
|
||||||
@ -37,21 +40,23 @@ static void prv_hello_world_lvgl(lv_obj_t* parent, NbScreenId screen_id) {
|
|||||||
label = lv_label_create(btn);
|
label = lv_label_create(btn);
|
||||||
lv_label_set_text_static(label, "Exit");
|
lv_label_set_text_static(label, "Exit");
|
||||||
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30);
|
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30);
|
||||||
lv_obj_add_event_cb(btn, prv_on_button_click, LV_EVENT_CLICKED, (void*)screen_id);
|
lv_obj_add_event_cb(btn, prv_on_button_click, LV_EVENT_CLICKED, NULL);
|
||||||
|
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
|
||||||
// TODO: on app exit, call gui_screen_destroy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t prv_hello_world_main(void* param) {
|
static int32_t prv_hello_world_main(void* param) {
|
||||||
UNUSED(param);
|
UNUSED(param);
|
||||||
|
|
||||||
// Open Gui record
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
NbGuiHandle gui = furi_record_open(RECORD_GUI);
|
|
||||||
|
|
||||||
// Register an lvgl screen
|
// Configure view port
|
||||||
gui_screen_create(gui, &prv_hello_world_lvgl);
|
view_port = view_port_alloc();
|
||||||
|
view_port_draw_callback_set(view_port, &prv_hello_world_lvgl, view_port);
|
||||||
|
|
||||||
|
// Register view port in GUI
|
||||||
|
NbGui* gui = furi_record_open(RECORD_GUI);
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
// Close Gui record
|
// Close Gui record
|
||||||
furi_record_close(RECORD_GUI);
|
furi_record_close(RECORD_GUI);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
CONFIG_IDF_TARGET="esp32"
|
CONFIG_IDF_TARGET="esp32"
|
||||||
CONFIG_LV_COLOR_16_SWAP=y
|
CONFIG_LV_COLOR_16_SWAP=y
|
||||||
CONFIG_LV_USE_USER_DATA=y
|
CONFIG_LV_USE_USER_DATA=y
|
||||||
|
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user