From 2d09b8e3181b80d6401605900977cc320e51d312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 10 Feb 2021 12:06:29 +0300 Subject: [PATCH] [FL-867] GUI: ViewPort arrangement API, better input and draw dispatching (#333) * Input: refactoring, platform agnostic key configuration, update usage across project. Minor queue usage fixes and tick timings. * Gui: lighter and more efficient input and draw call dispatching, ViewPort rearranging API. View: conditional model updates, API usage update. * BT: smaller update delay * GUI: ViewPort visibility check --- applications/bt/bt.c | 2 +- applications/dolphin/dolphin.c | 9 +- applications/gui/gui.c | 167 +++++++++++++++++++------- applications/gui/gui.h | 35 +++--- applications/gui/gui_event.c | 74 ------------ applications/gui/gui_event.h | 33 ----- applications/gui/gui_i.h | 57 ++++++++- applications/gui/modules/dialog.c | 20 ++- applications/gui/modules/dialog_ex.c | 21 +++- applications/gui/modules/popup.c | 4 + applications/gui/modules/submenu.c | 10 +- applications/gui/modules/text_input.c | 12 +- applications/gui/view.c | 4 +- applications/gui/view.h | 17 +-- applications/gui/view_port.c | 4 +- applications/menu/menu.c | 8 +- applications/nfc/nfc.c | 17 ++- applications/power/power.c | 4 +- 18 files changed, 299 insertions(+), 199 deletions(-) delete mode 100644 applications/gui/gui_event.c delete mode 100644 applications/gui/gui_event.h diff --git a/applications/bt/bt.c b/applications/bt/bt.c index 1621331e..eb8c6d4c 100644 --- a/applications/bt/bt.c +++ b/applications/bt/bt.c @@ -46,6 +46,6 @@ void bt_task() { while(1) { view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive()); - osDelay(1000); + osDelay(1024); } } diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c index ec52f931..9a89f4ed 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/dolphin/dolphin.c @@ -9,12 +9,15 @@ bool dolphin_view_first_start_input(InputEvent* event, void* context) { with_view_model( dolphin->idle_view_first_start, (DolphinViewFirstStartModel * model) { if(model->page > 0) model->page--; + return true; }); } else if(event->key == InputKeyRight) { uint32_t page; with_view_model( - dolphin->idle_view_first_start, - (DolphinViewFirstStartModel * model) { page = ++model->page; }); + dolphin->idle_view_first_start, (DolphinViewFirstStartModel * model) { + page = ++model->page; + return true; + }); if(page > 8) { dolphin_save(dolphin); view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); @@ -140,6 +143,7 @@ void dolphin_task() { dolphin->idle_view_stats, (DolphinViewIdleStatsModel * model) { model->icounter = dolphin_state_get_icounter(dolphin->state); model->butthurt = dolphin_state_get_butthurt(dolphin->state); + return true; }); furi_record_create("dolphin", dolphin); @@ -153,6 +157,7 @@ void dolphin_task() { dolphin->idle_view_stats, (DolphinViewIdleStatsModel * model) { model->icounter = dolphin_state_get_icounter(dolphin->state); model->butthurt = dolphin_state_get_butthurt(dolphin->state); + return true; }); } else if(event.type == DolphinEventTypeSave) { dolphin_state_save(dolphin->state); diff --git a/applications/gui/gui.c b/applications/gui/gui.c index 719adc63..c80ee1a1 100644 --- a/applications/gui/gui.c +++ b/applications/gui/gui.c @@ -1,41 +1,44 @@ -#include "gui.h" #include "gui_i.h" -#include -#include -#include - -#include "gui_event.h" -#include "canvas.h" -#include "canvas_i.h" -#include "view_port.h" -#include "view_port_i.h" - -ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); - -struct Gui { - GuiEvent* event; - Canvas* canvas; - ViewPortArray_t layers[GuiLayerMAX]; - osMutexId_t mutex; -}; - ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { - size_t view_ports_count = ViewPortArray_size(array); - for(size_t i = 0; i < view_ports_count; i++) { - ViewPort* view_port = *ViewPortArray_get(array, view_ports_count - i - 1); + // 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_update(Gui* gui) { +void gui_update(Gui* gui, ViewPort* view_port) { furi_assert(gui); - GuiMessage message; - message.type = GuiMessageTypeRedraw; - gui_event_messsage_send(gui->event, &message); + if(view_port) { + // Visibility check + gui_lock(gui); + for(size_t i = 0; i < GuiLayerMAX; i++) { + if(gui_view_port_find_enabled(gui->layers[i]) == view_port) { + osThreadFlagsSet(gui->thread, GUI_THREAD_FLAG_DRAW); + break; + } + } + gui_unlock(gui); + } else { + osThreadFlagsSet(gui->thread, GUI_THREAD_FLAG_DRAW); + } +} + +void gui_input_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + + Gui* gui = ctx; + + osMessageQueuePut(gui->input_queue, value, 0, osWaitForever); + osThreadFlagsSet(gui->thread, GUI_THREAD_FLAG_INPUT); } bool gui_redraw_fs(Gui* gui) { @@ -133,7 +136,9 @@ void gui_input(Gui* gui, InputEvent* input_event) { gui_lock(gui); - ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + ViewPort* view_port; + + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerMain]); if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]); @@ -160,10 +165,21 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { 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); - gui_update(gui); + + gui_update(gui, NULL); } void gui_remove_view_port(Gui* gui, ViewPort* view_port) { @@ -179,27 +195,87 @@ void gui_remove_view_port(Gui* gui, ViewPort* view_port) { while(!ViewPortArray_end_p(it)) { if(*ViewPortArray_ref(it) == view_port) { ViewPortArray_remove(gui->layers[i], it); + } else { + ViewPortArray_next(it); } - ViewPortArray_next(it); } } gui_unlock(gui); } +void gui_send_view_port_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); +} + +void gui_send_view_port_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); +} + Gui* gui_alloc() { Gui* gui = furi_alloc(sizeof(Gui)); + // Thread ID + gui->thread = osThreadGetId(); + gui->mutex_attr.name = "mtx_gui"; + gui->mutex_attr.attr_bits |= osMutexRecursive; // Allocate mutex - gui->mutex = osMutexNew(NULL); + gui->mutex = osMutexNew(&gui->mutex_attr); furi_check(gui->mutex); - // Event dispatcher - gui->event = gui_event_alloc(); - // Drawing canvas - gui->canvas = canvas_init(); - // Compose Layers + // Layers for(size_t i = 0; i < GuiLayerMAX; i++) { ViewPortArray_init(gui->layers[i]); } + // Drawing canvas + gui->canvas = canvas_init(); + // Input + gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); + gui->input_events = furi_record_open("input_events"); + furi_check(gui->input_events); + subscribe_pubsub(gui->input_events, gui_input_events_callback, gui); return gui; } @@ -207,16 +283,23 @@ Gui* gui_alloc() { void gui_task(void* p) { Gui* gui = gui_alloc(); - // Create FURI record furi_record_create("gui", gui); - // Forever dispatch while(1) { - GuiMessage message = gui_event_message_next(gui->event); - if(message.type == GuiMessageTypeRedraw) { + uint32_t flags = osThreadFlagsWait(GUI_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); + // Process and dispatch input + if(flags & GUI_THREAD_FLAG_INPUT) { + // Process till queue become empty + InputEvent input_event; + while(osMessageQueueGet(gui->input_queue, &input_event, NULL, 0) == osOK) { + gui_input(gui, &input_event); + } + } + // Process and dispatch draw call + if(flags & GUI_THREAD_FLAG_DRAW) { + // Clear flags that arrived on input step + osThreadFlagsClear(GUI_THREAD_FLAG_DRAW); gui_redraw(gui); - } else if(message.type == GuiMessageTypeInput) { - gui_input(gui, &message.input); } } } diff --git a/applications/gui/gui.h b/applications/gui/gui.h index fd8ca26c..a7283e99 100644 --- a/applications/gui/gui.h +++ b/applications/gui/gui.h @@ -7,26 +7,13 @@ extern "C" { #endif -#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 -#define GUI_STATUS_BAR_HEIGHT 8 - -#define GUI_MAIN_X 0 -#define GUI_MAIN_Y 9 -#define GUI_MAIN_WIDTH GUI_DISPLAY_WIDTH -#define GUI_MAIN_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_MAIN_Y) - typedef enum { GuiLayerNone, /* Special layer for internal use only */ - GuiLayerStatusBarLeft, /* Status bar left-side view_port layer, auto-layout */ - GuiLayerStatusBarRight, /* Status bar right-side view_port layer, auto-layout */ - GuiLayerMain, /* Main view_port layer, status bar is shown */ - GuiLayerFullscreen, /* Fullscreen view_port layer */ + GuiLayerStatusBarLeft, /* Status bar left-side layer, auto-layout */ + GuiLayerStatusBarRight, /* Status bar right-side layer, auto-layout */ + GuiLayerMain, /* Main layer, status bar is shown */ + GuiLayerFullscreen, /* Fullscreen layer */ GuiLayerMAX /* Don't use or move, special value */ } GuiLayer; @@ -45,6 +32,20 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer); */ 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_send_view_port_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_send_view_port_back(Gui* gui, ViewPort* view_port); + #ifdef __cplusplus } #endif diff --git a/applications/gui/gui_event.c b/applications/gui/gui_event.c deleted file mode 100644 index eeeb0101..00000000 --- a/applications/gui/gui_event.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "gui_event.h" - -#include - -#define GUI_EVENT_MQUEUE_SIZE 8 - -struct GuiEvent { - PubSub* input_event_record; - osTimerId_t timer; - osMessageQueueId_t mqueue; -}; - -void gui_event_timer_callback(void* arg) { - assert(arg); - GuiEvent* gui_event = arg; - - GuiMessage message; - message.type = GuiMessageTypeRedraw; - - osMessageQueuePut(gui_event->mqueue, &message, 0, osWaitForever); -} - -void gui_event_input_events_callback(const void* value, void* ctx) { - furi_assert(value); - furi_assert(ctx); - - GuiEvent* gui_event = ctx; - - GuiMessage message; - message.type = GuiMessageTypeInput; - message.input = *(InputEvent*)value; - - osMessageQueuePut(gui_event->mqueue, &message, 0, osWaitForever); -} - -GuiEvent* gui_event_alloc() { - GuiEvent* gui_event = furi_alloc(sizeof(GuiEvent)); - - // Allocate message queue - gui_event->mqueue = osMessageQueueNew(GUI_EVENT_MQUEUE_SIZE, sizeof(GuiMessage), NULL); - furi_check(gui_event->mqueue); - - gui_event->timer = osTimerNew(gui_event_timer_callback, osTimerPeriodic, gui_event, NULL); - assert(gui_event->timer); - // osTimerStart(gui_event->timer, 1024 / 4); - - // Input - gui_event->input_event_record = furi_record_open("input_events"); - furi_check(gui_event->input_event_record != NULL); - subscribe_pubsub(gui_event->input_event_record, gui_event_input_events_callback, gui_event); - - return gui_event; -} - -void gui_event_free(GuiEvent* gui_event) { - furi_assert(gui_event); - furi_check(osMessageQueueDelete(gui_event->mqueue) == osOK); - free(gui_event); -} - -void gui_event_messsage_send(GuiEvent* gui_event, GuiMessage* message) { - furi_assert(gui_event); - furi_assert(message); - osMessageQueuePut(gui_event->mqueue, message, 0, 0); -} - -GuiMessage gui_event_message_next(GuiEvent* gui_event) { - furi_assert(gui_event); - GuiMessage message; - - furi_check(osMessageQueueGet(gui_event->mqueue, &message, NULL, osWaitForever) == osOK); - - return message; -} diff --git a/applications/gui/gui_event.h b/applications/gui/gui_event.h deleted file mode 100644 index d9846867..00000000 --- a/applications/gui/gui_event.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GuiMessageTypeRedraw = 0x00, - GuiMessageTypeInput = 0x01, -} GuiMessageType; - -typedef struct { - GuiMessageType type; - InputEvent input; - void* data; -} GuiMessage; - -typedef struct GuiEvent GuiEvent; - -GuiEvent* gui_event_alloc(); - -void gui_event_free(GuiEvent* gui_event); - -void gui_event_messsage_send(GuiEvent* gui_event, GuiMessage* message); - -GuiMessage gui_event_message_next(GuiEvent* gui_event); - -#ifdef __cplusplus -} -#endif diff --git a/applications/gui/gui_i.h b/applications/gui/gui_i.h index 4200e7e1..2f9d3c91 100644 --- a/applications/gui/gui_i.h +++ b/applications/gui/gui_i.h @@ -1,7 +1,60 @@ #pragma once -typedef struct Gui Gui; +#include "gui.h" + +#include +#include +#include + +#include "canvas.h" +#include "canvas_i.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 +#define GUI_STATUS_BAR_HEIGHT 8 + +#define GUI_MAIN_X 0 +#define GUI_MAIN_Y 9 +#define GUI_MAIN_WIDTH GUI_DISPLAY_WIDTH +#define GUI_MAIN_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_MAIN_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); + +struct Gui { + // Thread and lock + osThreadId_t thread; + osMutexAttr_t mutex_attr; + osMutexId_t mutex; + // Layers and Canvas + ViewPortArray_t layers[GuiLayerMAX]; + Canvas* canvas; + // Input + osMessageQueueId_t input_queue; + PubSub* input_events; +}; + +ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); + +/* Update GUI, request redraw + * Real redraw event will be issued only if view_port is currently visible + * Setting view_port to NULL forces redraw, but must be avoided + * @param gui, Gui instance + * @param view_port, ViewPort instance or NULL + */ +void gui_update(Gui* gui, ViewPort* view_port); + +void gui_input_events_callback(const void* value, void* ctx); -void gui_update(Gui* gui); void gui_lock(Gui* gui); + void gui_unlock(Gui* gui); \ No newline at end of file diff --git a/applications/gui/modules/dialog.c b/applications/gui/modules/dialog.c index df20ad9a..b7469648 100644 --- a/applications/gui/modules/dialog.c +++ b/applications/gui/modules/dialog.c @@ -91,26 +91,38 @@ void dialog_set_header_text(Dialog* dialog, const char* text) { furi_assert(dialog); furi_assert(text); with_view_model( - dialog->view, (DialogModel * model) { model->header_text = text; }); + dialog->view, (DialogModel * model) { + model->header_text = text; + return true; + }); } void dialog_set_text(Dialog* dialog, const char* text) { furi_assert(dialog); furi_assert(text); with_view_model( - dialog->view, (DialogModel * model) { model->text = text; }); + dialog->view, (DialogModel * model) { + model->text = text; + return true; + }); } void dialog_set_left_button_text(Dialog* dialog, const char* text) { furi_assert(dialog); furi_assert(text); with_view_model( - dialog->view, (DialogModel * model) { model->left_text = text; }); + dialog->view, (DialogModel * model) { + model->left_text = text; + return true; + }); } void dialog_set_right_button_text(Dialog* dialog, const char* text) { furi_assert(dialog); furi_assert(text); with_view_model( - dialog->view, (DialogModel * model) { model->right_text = text; }); + dialog->view, (DialogModel * model) { + model->right_text = text; + return true; + }); } diff --git a/applications/gui/modules/dialog_ex.c b/applications/gui/modules/dialog_ex.c index 4fbbc4f4..0448a205 100644 --- a/applications/gui/modules/dialog_ex.c +++ b/applications/gui/modules/dialog_ex.c @@ -94,6 +94,7 @@ static bool dialog_ex_view_input_callback(InputEvent* event, void* context) { left_text = model->left_text; center_text = model->center_text; right_text = model->right_text; + return true; }); // Process key presses only @@ -142,6 +143,8 @@ DialogEx* dialog_ex_alloc() { model->left_text = NULL; model->center_text = NULL; model->right_text = NULL; + + return true; }); return dialog_ex; } @@ -182,6 +185,7 @@ void dialog_ex_set_header( model->header.y = y; model->header.horizontal = horizontal; model->header.vertical = vertical; + return true; }); } @@ -200,6 +204,7 @@ void dialog_ex_set_text( model->text.y = y; model->text.horizontal = horizontal; model->text.vertical = vertical; + return true; }); } @@ -210,23 +215,33 @@ void dialog_ex_set_icon(DialogEx* dialog_ex, int8_t x, int8_t y, IconName name) model->icon.x = x; model->icon.y = y; model->icon.name = name; + return true; }); } void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { model->left_text = text; }); + dialog_ex->view, (DialogExModel * model) { + model->left_text = text; + return true; + }); } void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { model->center_text = text; }); + dialog_ex->view, (DialogExModel * model) { + model->center_text = text; + return true; + }); } void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { model->right_text = text; }); + dialog_ex->view, (DialogExModel * model) { + model->right_text = text; + return true; + }); } diff --git a/applications/gui/modules/popup.c b/applications/gui/modules/popup.c index 8f79850c..1b6520de 100644 --- a/applications/gui/modules/popup.c +++ b/applications/gui/modules/popup.c @@ -141,6 +141,7 @@ Popup* popup_alloc() { model->icon.x = -1; model->icon.y = -1; model->icon.name = I_ButtonCenter_7x7; + return true; }); return popup; } @@ -182,6 +183,7 @@ void popup_set_header( model->header.y = y; model->header.horizontal = horizontal; model->header.vertical = vertical; + return true; }); } @@ -200,6 +202,7 @@ void popup_set_text( model->text.y = y; model->text.horizontal = horizontal; model->text.vertical = vertical; + return true; }); } @@ -210,6 +213,7 @@ void popup_set_icon(Popup* popup, int8_t x, int8_t y, IconName name) { model->icon.x = x; model->icon.y = y; model->icon.name = name; + return true; }); } diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c index d2873c8c..df1584df 100644 --- a/applications/gui/modules/submenu.c +++ b/applications/gui/modules/submenu.c @@ -108,6 +108,7 @@ Submenu* submenu_alloc() { submenu->view, (SubmenuModel * model) { SubmenuItemArray_init(model->items); model->position = 0; + return true; }); return submenu; @@ -117,7 +118,10 @@ void submenu_free(Submenu* submenu) { furi_assert(submenu); with_view_model( - submenu->view, (SubmenuModel * model) { SubmenuItemArray_clear(model->items); }); + submenu->view, (SubmenuModel * model) { + SubmenuItemArray_clear(model->items); + return true; + }); view_free(submenu->view); free(submenu); } @@ -142,6 +146,7 @@ SubmenuItem* submenu_add_item( item->label = label; item->callback = callback; item->callback_context = callback_context; + return true; }); return item; @@ -159,6 +164,7 @@ void submenu_process_up(Submenu* submenu) { model->position = SubmenuItemArray_size(model->items) - 1; model->window_position = model->position - 3; } + return true; }); } @@ -175,6 +181,7 @@ void submenu_process_down(Submenu* submenu) { model->position = 0; model->window_position = 0; } + return true; }); } @@ -186,6 +193,7 @@ void submenu_process_ok(Submenu* submenu) { if(model->position < (SubmenuItemArray_size(model->items))) { item = SubmenuItemArray_get(model->items, model->position); } + return true; }); if(item && item->callback) { diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index 88441b84..d08071a8 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -223,6 +223,7 @@ static void text_input_handle_up(TextInput* text_input) { if(model->selected_row > 0) { model->selected_row--; } + return true; }); } @@ -235,6 +236,7 @@ static void text_input_handle_down(TextInput* text_input) { model->selected_column = get_row_size(model->selected_row) - 1; } } + return true; }); } @@ -246,6 +248,7 @@ static void text_input_handle_left(TextInput* text_input) { } else { model->selected_column = get_row_size(model->selected_row) - 1; } + return true; }); } @@ -257,6 +260,7 @@ static void text_input_handle_right(TextInput* text_input) { } else { model->selected_column = 0; } + return true; }); } @@ -281,6 +285,7 @@ static void text_input_handle_ok(TextInput* text_input) { model->text[text_length] = selected; model->text[text_length + 1] = 0; } + return true; }); } @@ -333,6 +338,7 @@ TextInput* text_input_alloc() { model->header = ""; model->selected_row = 0; model->selected_column = 0; + return true; }); return text_input; @@ -361,10 +367,14 @@ void text_input_set_result_callback( model->callback_context = callback_context; model->text = text; model->max_text_length = max_text_length; + return true; }); } void text_input_set_header_text(TextInput* text_input, const char* text) { with_view_model( - text_input->view, (TextInputModel * model) { model->header = text; }); + text_input->view, (TextInputModel * model) { + model->header = text; + return true; + }); } \ No newline at end of file diff --git a/applications/gui/view.c b/applications/gui/view.c index 93038368..d2838ba3 100644 --- a/applications/gui/view.c +++ b/applications/gui/view.c @@ -102,10 +102,10 @@ void* view_get_model(View* view) { return view->model; } -void view_commit_model(View* view) { +void view_commit_model(View* view, bool update) { furi_assert(view); view_unlock_model(view); - if(view->dispatcher) { + if(update && view->dispatcher) { view_dispatcher_update(view->dispatcher, view); } } diff --git a/applications/gui/view.h b/applications/gui/view.h index 2b2950da..ccd6cadd 100644 --- a/applications/gui/view.h +++ b/applications/gui/view.h @@ -137,20 +137,21 @@ void* view_get_model(View* view); /* Commit view model * @param view, pointer to View + * @param update, true if you want to emit view update, false otherwise */ -void view_commit_model(View* view); +void view_commit_model(View* view, bool update); /* * With clause for view model * @param view, View instance pointer - * @param function_body a (){} lambda declaration, - * executed within you parent function context. + * @param function_body a (){} lambda declaration, executed within you parent function context + * @return true if you want to emit view update, false otherwise */ -#define with_view_model(view, function_body) \ - { \ - void* p = view_get_model(view); \ - ({ void __fn__ function_body __fn__; })(p); \ - view_commit_model(view); \ +#define with_view_model(view, function_body) \ + { \ + void* p = view_get_model(view); \ + bool update = ({ bool __fn__ function_body __fn__; })(p); \ + view_commit_model(view, update); \ } #ifdef __cplusplus diff --git a/applications/gui/view_port.c b/applications/gui/view_port.c index 961ed044..6c5e0820 100644 --- a/applications/gui/view_port.c +++ b/applications/gui/view_port.c @@ -43,7 +43,7 @@ void view_port_enabled_set(ViewPort* view_port, bool enabled) { furi_assert(view_port); if(view_port->is_enabled != enabled) { view_port->is_enabled = enabled; - view_port_update(view_port); + if(view_port->gui) gui_update(view_port->gui, NULL); } } @@ -69,7 +69,7 @@ void view_port_input_callback_set( void view_port_update(ViewPort* view_port) { furi_assert(view_port); - if(view_port->gui) gui_update(view_port->gui); + if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui, view_port); } void view_port_gui_set(ViewPort* view_port, Gui* gui) { diff --git a/applications/menu/menu.c b/applications/menu/menu.c index 8ce2b8f0..b39363e6 100644 --- a/applications/menu/menu.c +++ b/applications/menu/menu.c @@ -14,6 +14,7 @@ struct Menu { MenuEvent* event; // GUI + Gui* gui; ViewPort* view_port; Icon* icon; @@ -37,12 +38,14 @@ ValueMutex* menu_init() { furiac_exit(NULL); } + // OpenGui record + menu->gui = furi_record_open("gui"); + // Allocate and configure view_port menu->view_port = view_port_alloc(); // Open GUI and register fullscreen view_port - Gui* gui = furi_record_open("gui"); - gui_add_view_port(gui, menu->view_port, GuiLayerFullscreen); + gui_add_view_port(menu->gui, menu->view_port, GuiLayerFullscreen); view_port_enabled_set(menu->view_port, false); view_port_draw_callback_set(menu->view_port, menu_view_port_callback, menu_mutex); @@ -198,6 +201,7 @@ void menu_ok(Menu* menu) { menu_update(menu); } else if(type == MenuItemTypeFunction) { menu_item_function_call(item); + gui_send_view_port_back(menu->gui, menu->view_port); } } diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 98abc2ac..bc97a8e4 100644 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -89,8 +89,10 @@ void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state) { NfcWorkerState state = nfc_worker_get_state(nfc->worker); if(state == NfcWorkerStateBroken) { with_view_model( - nfc->view_error, - (NfcViewErrorModel * model) { model->error = nfc_worker_get_error(nfc->worker); }); + nfc->view_error, (NfcViewErrorModel * model) { + model->error = nfc_worker_get_error(nfc->worker); + return true; + }); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewError); } else if(state == NfcWorkerStateReady) { view_dispatcher_switch_to_view(nfc->view_dispatcher, view_id); @@ -114,7 +116,10 @@ void nfc_task(void* p) { furi_check(osMessageQueueGet(nfc->message_queue, &message, NULL, osWaitForever) == osOK); if(message.type == NfcMessageTypeDetect) { with_view_model( - nfc->view_detect, (NfcViewReadModel * model) { model->found = false; }); + nfc->view_detect, (NfcViewReadModel * model) { + model->found = false; + return true; + }); nfc_start(nfc, NfcViewRead, NfcWorkerStatePoll); } else if(message.type == NfcMessageTypeEmulate) { nfc_start(nfc, NfcViewEmulate, NfcWorkerStateEmulate); @@ -127,10 +132,14 @@ void nfc_task(void* p) { nfc->view_detect, (NfcViewReadModel * model) { model->found = true; model->device = message.device; + return true; }); } else if(message.type == NfcMessageTypeDeviceNotFound) { with_view_model( - nfc->view_detect, (NfcViewReadModel * model) { model->found = false; }); + nfc->view_detect, (NfcViewReadModel * model) { + model->found = false; + return true; + }); } } } diff --git a/applications/power/power.c b/applications/power/power.c index ab336cec..05a7c945 100644 --- a/applications/power/power.c +++ b/applications/power/power.c @@ -46,6 +46,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { with_view_model( power->info_view, (PowerInfoModel * model) { canvas_draw_box(canvas, 2, 2, (float)model->charge / 100 * 14, 4); + return false; }); } @@ -215,10 +216,11 @@ void power_task(void* p) { api_hal_power_get_battery_temperature(ApiHalPowerICCharger); model->temperature_gauge = api_hal_power_get_battery_temperature(ApiHalPowerICFuelGauge); + return true; }); view_port_update(power->battery_view_port); view_port_enabled_set(power->usb_view_port, api_hal_power_is_charging()); - osDelay(1000); + osDelay(1024); } }