GUI: status bar rendering. Power: battery indicator. (#207)
* Menu: animation. Irukagotchi: idle image. * Power: battery, usb activity widget * Power: tune battery max voltage and clamp overshoot * get initial charge state Co-authored-by: Aleksandr Kutuzov <aku@plooks.com> Co-authored-by: aanper <mail@s3f.ru>
This commit is contained in:
		
							parent
							
								
									8aeafd8179
								
							
						
					
					
						commit
						0af239ebc0
					
				| @ -2,6 +2,7 @@ | |||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include "menu/menu.h" | #include "menu/menu.h" | ||||||
| #include "applications.h" | #include "applications.h" | ||||||
|  | #include <assets_icons.h> | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriApp* handler; |     FuriApp* handler; | ||||||
| @ -87,7 +88,9 @@ void app_loader(void* p) { | |||||||
|             ctx->app = &FLIPPER_APPS[i]; |             ctx->app = &FLIPPER_APPS[i]; | ||||||
| 
 | 
 | ||||||
|             menu_item_add( |             menu_item_add( | ||||||
|                 menu, menu_item_alloc_function(FLIPPER_APPS[i].name, NULL, handle_menu, ctx)); |                 menu, | ||||||
|  |                 menu_item_alloc_function( | ||||||
|  |                     FLIPPER_APPS[i].name, assets_icons_get(A_Infrared_14), handle_menu, ctx)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /*
 |         /*
 | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ void cc1101_workaround(void* p); | |||||||
| void lf_rfid_workaround(void* p); | void lf_rfid_workaround(void* p); | ||||||
| void nfc_task(void* p); | void nfc_task(void* p); | ||||||
| void irukagotchi_task(void* p); | void irukagotchi_task(void* p); | ||||||
|  | void power_task(void* p); | ||||||
| 
 | 
 | ||||||
| const FlipperStartupApp FLIPPER_STARTUP[] = { | const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||||
| #ifdef APP_DISPLAY | #ifdef APP_DISPLAY | ||||||
| @ -59,6 +60,10 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | |||||||
|     {.app = irukagotchi_task, .name = "irukagotchi_task", .libs = {1, FURI_LIB{"menu_task"}}}, |     {.app = irukagotchi_task, .name = "irukagotchi_task", .libs = {1, FURI_LIB{"menu_task"}}}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_POWER | ||||||
|  |     {.app = power_task, .name = "power_task", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_CC1101 | #ifdef APP_CC1101 | ||||||
|     {.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}}, |     {.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ APP_RELEASE ?= 0 | |||||||
| ifeq ($(APP_RELEASE), 1) | ifeq ($(APP_RELEASE), 1) | ||||||
| APP_MENU = 1 | APP_MENU = 1 | ||||||
| APP_NFC  = 1 | APP_NFC  = 1 | ||||||
|  | APP_POWER = 1 | ||||||
| BUILD_IRDA  = 1 | BUILD_IRDA  = 1 | ||||||
| APP_IRUKAGOTCHI = 1 | APP_IRUKAGOTCHI = 1 | ||||||
| BUILD_EXAMPLE_BLINK = 1 | BUILD_EXAMPLE_BLINK = 1 | ||||||
| @ -34,6 +35,13 @@ CFLAGS		+= -DAPP_IRUKAGOTCHI | |||||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/irukagotchi/*.c) | C_SOURCES	+= $(wildcard $(APP_DIR)/irukagotchi/*.c) | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | APP_POWER ?= 0 | ||||||
|  | ifeq ($(APP_POWER), 1) | ||||||
|  | APP_GUI		= 1 | ||||||
|  | CFLAGS		+= -DAPP_POWER | ||||||
|  | C_SOURCES	+= $(wildcard $(APP_DIR)/power/*.c) | ||||||
|  | endif | ||||||
|  | 
 | ||||||
| APP_MENU ?= 0 | APP_MENU ?= 0 | ||||||
| ifeq ($(APP_MENU), 1) | ifeq ($(APP_MENU), 1) | ||||||
| CFLAGS += -DAPP_MENU | CFLAGS += -DAPP_MENU | ||||||
|  | |||||||
| @ -64,6 +64,12 @@ void canvas_api_free(CanvasApi* api) { | |||||||
|     free(api); |     free(api); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void canvas_reset(CanvasApi* api) { | ||||||
|  |     assert(api); | ||||||
|  |     canvas_color_set(api, ColorBlack); | ||||||
|  |     canvas_font_set(api, FontSecondary); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void canvas_commit(CanvasApi* api) { | void canvas_commit(CanvasApi* api) { | ||||||
|     furi_assert(api); |     furi_assert(api); | ||||||
|     Canvas* canvas = (Canvas*)api; |     Canvas* canvas = (Canvas*)api; | ||||||
| @ -144,23 +150,33 @@ void canvas_icon_draw(CanvasApi* api, uint8_t x, uint8_t y, Icon* icon) { | |||||||
| void canvas_dot_draw(CanvasApi* api, uint8_t x, uint8_t y) { | void canvas_dot_draw(CanvasApi* api, uint8_t x, uint8_t y) { | ||||||
|     furi_assert(api); |     furi_assert(api); | ||||||
|     Canvas* canvas = (Canvas*)api; |     Canvas* canvas = (Canvas*)api; | ||||||
|  |     x += canvas->offset_x; | ||||||
|  |     y += canvas->offset_y; | ||||||
|     u8g2_DrawPixel(&canvas->fb, x, y); |     u8g2_DrawPixel(&canvas->fb, x, y); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_box_draw(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | void canvas_box_draw(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | ||||||
|     furi_assert(api); |     furi_assert(api); | ||||||
|     Canvas* canvas = (Canvas*)api; |     Canvas* canvas = (Canvas*)api; | ||||||
|  |     x += canvas->offset_x; | ||||||
|  |     y += canvas->offset_y; | ||||||
|     u8g2_DrawBox(&canvas->fb, x, y, width, height); |     u8g2_DrawBox(&canvas->fb, x, y, width, height); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_draw_frame(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | void canvas_draw_frame(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | ||||||
|     furi_assert(api); |     furi_assert(api); | ||||||
|     Canvas* canvas = (Canvas*)api; |     Canvas* canvas = (Canvas*)api; | ||||||
|  |     x += canvas->offset_x; | ||||||
|  |     y += canvas->offset_y; | ||||||
|     u8g2_DrawFrame(&canvas->fb, x, y, width, height); |     u8g2_DrawFrame(&canvas->fb, x, y, width, height); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void canvas_draw_line(CanvasApi* api, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { | void canvas_draw_line(CanvasApi* api, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { | ||||||
|     furi_assert(api); |     furi_assert(api); | ||||||
|     Canvas* canvas = (Canvas*)api; |     Canvas* canvas = (Canvas*)api; | ||||||
|  |     x1 += canvas->offset_x; | ||||||
|  |     y1 += canvas->offset_y; | ||||||
|  |     x2 += canvas->offset_x; | ||||||
|  |     y2 += canvas->offset_y; | ||||||
|     u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2); |     u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2); | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ CanvasApi* canvas_api_init(); | |||||||
| 
 | 
 | ||||||
| void canvas_api_free(CanvasApi* api); | void canvas_api_free(CanvasApi* api); | ||||||
| 
 | 
 | ||||||
|  | void canvas_reset(CanvasApi* api); | ||||||
|  | 
 | ||||||
| void canvas_commit(CanvasApi* api); | void canvas_commit(CanvasApi* api); | ||||||
| 
 | 
 | ||||||
| void canvas_frame_set( | void canvas_frame_set( | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ void gui_update(Gui* gui) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool gui_redraw_fs(Gui* gui) { | bool gui_redraw_fs(Gui* gui) { | ||||||
|     canvas_frame_set(gui->canvas_api, 0, 0, 128, 64); |     canvas_frame_set(gui->canvas_api, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); | ||||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerFullscreen]); |     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||||
|     if(widget) { |     if(widget) { | ||||||
|         widget_draw(widget, gui->canvas_api); |         widget_draw(widget, gui->canvas_api); | ||||||
| @ -51,18 +51,48 @@ bool gui_redraw_fs(Gui* gui) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool gui_redraw_status_bar(Gui* gui) { | void gui_redraw_status_bar(Gui* gui) { | ||||||
|     canvas_frame_set(gui->canvas_api, 0, 0, 128, 64); |     WidgetArray_it_t it; | ||||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerStatusBar]); |     uint8_t x; | ||||||
|     if(widget) { |     uint8_t x_used = 0; | ||||||
|  |     uint8_t width; | ||||||
|  |     Widget* widget; | ||||||
|  |     // Right side
 | ||||||
|  |     x = 128; | ||||||
|  |     WidgetArray_it(it, gui->layers[GuiLayerStatusBarRight]); | ||||||
|  |     while(!WidgetArray_end_p(it) && x_used < GUI_STATUS_BAR_WIDTH) { | ||||||
|  |         // Render widget;
 | ||||||
|  |         widget = *WidgetArray_ref(it); | ||||||
|  |         if(widget_is_enabled(widget)) { | ||||||
|  |             width = widget_get_width(widget); | ||||||
|  |             if(!width) width = 8; | ||||||
|  |             x_used += width; | ||||||
|  |             x -= width; | ||||||
|  |             canvas_frame_set(gui->canvas_api, x, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT); | ||||||
|             widget_draw(widget, gui->canvas_api); |             widget_draw(widget, gui->canvas_api); | ||||||
|         return true; |  | ||||||
|         } |         } | ||||||
|     return false; |         WidgetArray_next(it); | ||||||
|  |     } | ||||||
|  |     // Left side
 | ||||||
|  |     x = 0; | ||||||
|  |     WidgetArray_it(it, gui->layers[GuiLayerStatusBarLeft]); | ||||||
|  |     while(!WidgetArray_end_p(it) && x_used < GUI_STATUS_BAR_WIDTH) { | ||||||
|  |         // Render widget;
 | ||||||
|  |         widget = *WidgetArray_ref(it); | ||||||
|  |         if(widget_is_enabled(widget)) { | ||||||
|  |             width = widget_get_width(widget); | ||||||
|  |             if(!width) width = 8; | ||||||
|  |             x_used += width; | ||||||
|  |             canvas_frame_set(gui->canvas_api, x, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT); | ||||||
|  |             widget_draw(widget, gui->canvas_api); | ||||||
|  |             x += width; | ||||||
|  |         } | ||||||
|  |         WidgetArray_next(it); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool gui_redraw_normal(Gui* gui) { | bool gui_redraw_normal(Gui* gui) { | ||||||
|     canvas_frame_set(gui->canvas_api, 0, 9, 128, 55); |     canvas_frame_set(gui->canvas_api, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT); | ||||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerMain]); |     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerMain]); | ||||||
|     if(widget) { |     if(widget) { | ||||||
|         widget_draw(widget, gui->canvas_api); |         widget_draw(widget, gui->canvas_api); | ||||||
| @ -72,7 +102,7 @@ bool gui_redraw_normal(Gui* gui) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool gui_redraw_none(Gui* gui) { | bool gui_redraw_none(Gui* gui) { | ||||||
|     canvas_frame_set(gui->canvas_api, 0, 9, 118, 44); |     canvas_frame_set(gui->canvas_api, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT); | ||||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerNone]); |     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerNone]); | ||||||
|     if(widget) { |     if(widget) { | ||||||
|         widget_draw(widget, gui->canvas_api); |         widget_draw(widget, gui->canvas_api); | ||||||
| @ -86,6 +116,8 @@ void gui_redraw(Gui* gui) { | |||||||
|     furi_assert(gui); |     furi_assert(gui); | ||||||
|     gui_lock(gui); |     gui_lock(gui); | ||||||
| 
 | 
 | ||||||
|  |     canvas_reset(gui->canvas_api); | ||||||
|  | 
 | ||||||
|     if(!gui_redraw_fs(gui)) { |     if(!gui_redraw_fs(gui)) { | ||||||
|         if(!gui_redraw_normal(gui)) { |         if(!gui_redraw_normal(gui)) { | ||||||
|             gui_redraw_none(gui); |             gui_redraw_none(gui); | ||||||
|  | |||||||
| @ -3,10 +3,24 @@ | |||||||
| #include "widget.h" | #include "widget.h" | ||||||
| #include "canvas.h" | #include "canvas.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) | ||||||
|  | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     GuiLayerNone, /* Special layer for internal use only */ |     GuiLayerNone, /* Special layer for internal use only */ | ||||||
| 
 | 
 | ||||||
|     GuiLayerStatusBar, /* Status bar widget layer */ |     GuiLayerStatusBarLeft, /* Status bar left-side widget layer, auto-layout */ | ||||||
|  |     GuiLayerStatusBarRight, /* Status bar right-side widget layer, auto-layout */ | ||||||
|     GuiLayerMain, /* Main widget layer, status bar is shown */ |     GuiLayerMain, /* Main widget layer, status bar is shown */ | ||||||
|     GuiLayerFullscreen, /* Fullscreen widget layer */ |     GuiLayerFullscreen, /* Fullscreen widget layer */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,9 +6,20 @@ | |||||||
| 
 | 
 | ||||||
| struct GuiEvent { | struct GuiEvent { | ||||||
|     PubSub* input_event_record; |     PubSub* input_event_record; | ||||||
|  |     osTimerId_t timer; | ||||||
|     osMessageQueueId_t mqueue; |     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) { | void gui_event_input_events_callback(const void* value, void* ctx) { | ||||||
|     furi_assert(value); |     furi_assert(value); | ||||||
|     furi_assert(ctx); |     furi_assert(ctx); | ||||||
| @ -29,6 +40,10 @@ GuiEvent* gui_event_alloc() { | |||||||
|     gui_event->mqueue = osMessageQueueNew(GUI_EVENT_MQUEUE_SIZE, sizeof(GuiMessage), NULL); |     gui_event->mqueue = osMessageQueueNew(GUI_EVENT_MQUEUE_SIZE, sizeof(GuiMessage), NULL); | ||||||
|     furi_check(gui_event->mqueue); |     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, 1000 / 10); | ||||||
|  | 
 | ||||||
|     // Input
 |     // Input
 | ||||||
|     gui_event->input_event_record = furi_open("input_events"); |     gui_event->input_event_record = furi_open("input_events"); | ||||||
|     furi_check(gui_event->input_event_record != NULL); |     furi_check(gui_event->input_event_record != NULL); | ||||||
|  | |||||||
| @ -10,15 +10,6 @@ | |||||||
| 
 | 
 | ||||||
| // TODO add mutex to widget ops
 | // TODO add mutex to widget ops
 | ||||||
| 
 | 
 | ||||||
| struct Widget { |  | ||||||
|     Gui* gui; |  | ||||||
|     bool is_enabled; |  | ||||||
|     WidgetDrawCallback draw_callback; |  | ||||||
|     void* draw_callback_context; |  | ||||||
|     WidgetInputCallback input_callback; |  | ||||||
|     void* input_callback_context; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| Widget* widget_alloc(WidgetDrawCallback callback, void* callback_context) { | Widget* widget_alloc(WidgetDrawCallback callback, void* callback_context) { | ||||||
|     Widget* widget = furi_alloc(sizeof(Widget)); |     Widget* widget = furi_alloc(sizeof(Widget)); | ||||||
|     widget->is_enabled = true; |     widget->is_enabled = true; | ||||||
| @ -31,6 +22,26 @@ void widget_free(Widget* widget) { | |||||||
|     free(widget); |     free(widget); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void widget_set_width(Widget* widget, uint8_t width) { | ||||||
|  |     assert(widget); | ||||||
|  |     widget->width = width; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t widget_get_width(Widget* widget) { | ||||||
|  |     assert(widget); | ||||||
|  |     return widget->width; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void widget_set_height(Widget* widget, uint8_t height) { | ||||||
|  |     assert(widget); | ||||||
|  |     widget->height = height; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t widget_get_height(Widget* widget) { | ||||||
|  |     assert(widget); | ||||||
|  |     return widget->height; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void widget_enabled_set(Widget* widget, bool enabled) { | void widget_enabled_set(Widget* widget, bool enabled) { | ||||||
|     furi_assert(widget); |     furi_assert(widget); | ||||||
|     if(widget->is_enabled != enabled) { |     if(widget->is_enabled != enabled) { | ||||||
| @ -80,7 +91,9 @@ void widget_draw(Widget* widget, CanvasApi* canvas_api) { | |||||||
| void widget_input(Widget* widget, InputEvent* event) { | void widget_input(Widget* widget, InputEvent* event) { | ||||||
|     furi_assert(widget); |     furi_assert(widget); | ||||||
|     furi_assert(event); |     furi_assert(event); | ||||||
| 
 |  | ||||||
|     furi_check(widget->gui); |     furi_check(widget->gui); | ||||||
|     if(widget->input_callback) widget->input_callback(event, widget->input_callback_context); | 
 | ||||||
|  |     if(widget->input_callback) { | ||||||
|  |         widget->input_callback(event, widget->input_callback_context); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,14 +8,51 @@ typedef struct Widget Widget; | |||||||
| typedef void (*WidgetDrawCallback)(CanvasApi* api, void* context); | typedef void (*WidgetDrawCallback)(CanvasApi* api, void* context); | ||||||
| typedef void (*WidgetInputCallback)(InputEvent* event, void* context); | typedef void (*WidgetInputCallback)(InputEvent* event, void* context); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Widget allocator | ||||||
|  |  * always returns widget or stops system if not enough memory. | ||||||
|  |  */ | ||||||
| Widget* widget_alloc(); | Widget* widget_alloc(); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Widget deallocator | ||||||
|  |  * Ensure that widget was unregistered in GUI system before use. | ||||||
|  |  */ | ||||||
| void widget_free(Widget* widget); | void widget_free(Widget* widget); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Set widget width. | ||||||
|  |  * Will be used to limit canvas drawing area and autolayout feature. | ||||||
|  |  * @param width - wanted width, 0 - auto. | ||||||
|  |  */ | ||||||
|  | void widget_set_width(Widget* widget, uint8_t width); | ||||||
|  | uint8_t widget_get_width(Widget* widget); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Set widget height. | ||||||
|  |  * Will be used to limit canvas drawing area and autolayout feature. | ||||||
|  |  * @param height - wanted height, 0 - auto. | ||||||
|  |  */ | ||||||
|  | void widget_set_height(Widget* widget, uint8_t height); | ||||||
|  | uint8_t widget_get_height(Widget* widget); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Enable or disable widget rendering. | ||||||
|  |  * @param enabled. | ||||||
|  |  */ | ||||||
| void widget_enabled_set(Widget* widget, bool enabled); | void widget_enabled_set(Widget* widget, bool enabled); | ||||||
| bool widget_is_enabled(Widget* widget); | bool widget_is_enabled(Widget* widget); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Widget event callbacks | ||||||
|  |  * @param callback - appropriate callback function | ||||||
|  |  * @param context - context to pass to callback | ||||||
|  |  */ | ||||||
| void widget_draw_callback_set(Widget* widget, WidgetDrawCallback callback, void* context); | void widget_draw_callback_set(Widget* widget, WidgetDrawCallback callback, void* context); | ||||||
| void widget_input_callback_set(Widget* widget, WidgetInputCallback callback, void* context); | void widget_input_callback_set(Widget* widget, WidgetInputCallback callback, void* context); | ||||||
| 
 | 
 | ||||||
| // emit update signal
 | /*
 | ||||||
|  |  * Emit update signal to GUI system. | ||||||
|  |  * Rendering will happen later after GUI system process signal. | ||||||
|  |  */ | ||||||
| void widget_update(Widget* widget); | void widget_update(Widget* widget); | ||||||
|  | |||||||
| @ -2,8 +2,31 @@ | |||||||
| 
 | 
 | ||||||
| #include "gui_i.h" | #include "gui_i.h" | ||||||
| 
 | 
 | ||||||
|  | struct Widget { | ||||||
|  |     Gui* gui; | ||||||
|  |     bool is_enabled; | ||||||
|  |     uint8_t width; | ||||||
|  |     uint8_t height; | ||||||
|  |     WidgetDrawCallback draw_callback; | ||||||
|  |     void* draw_callback_context; | ||||||
|  |     WidgetInputCallback input_callback; | ||||||
|  |     void* input_callback_context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Set GUI referenec. | ||||||
|  |  * @param gui - gui instance pointer. | ||||||
|  |  */ | ||||||
| void widget_gui_set(Widget* widget, Gui* gui); | void widget_gui_set(Widget* widget, Gui* gui); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Process draw call. Calls draw callback. | ||||||
|  |  * @param canvas_api - canvas to draw at. | ||||||
|  |  */ | ||||||
| void widget_draw(Widget* widget, CanvasApi* canvas_api); | void widget_draw(Widget* widget, CanvasApi* canvas_api); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Process input. Calls input callback. | ||||||
|  |  * @param event - pointer to input event. | ||||||
|  |  */ | ||||||
| void widget_input(Widget* widget, InputEvent* event); | void widget_input(Widget* widget, InputEvent* event); | ||||||
|  | |||||||
| @ -21,8 +21,9 @@ void irukagotchi_draw_callback(CanvasApi* canvas, void* context) { | |||||||
|     canvas->clear(canvas); |     canvas->clear(canvas); | ||||||
|     canvas->set_color(canvas, ColorBlack); |     canvas->set_color(canvas, ColorBlack); | ||||||
|     canvas->set_font(canvas, FontPrimary); |     canvas->set_font(canvas, FontPrimary); | ||||||
|     canvas->draw_icon(canvas, 10, 20, irukagotchi->icon); |     canvas->draw_icon(canvas, 0, 0, irukagotchi->icon); | ||||||
|     canvas->draw_str(canvas, 30, 32, "Irukagotchi"); |     canvas->draw_str(canvas, 80, 30, "111001"); | ||||||
|  |     canvas->draw_str(canvas, 80, 42, "011010"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void irukagotchi_input_callback(InputEvent* event, void* context) { | void irukagotchi_input_callback(InputEvent* event, void* context) { | ||||||
| @ -37,7 +38,7 @@ void irukagotchi_input_callback(InputEvent* event, void* context) { | |||||||
| Irukagotchi* irukagotchi_alloc() { | Irukagotchi* irukagotchi_alloc() { | ||||||
|     Irukagotchi* irukagotchi = furi_alloc(sizeof(Irukagotchi)); |     Irukagotchi* irukagotchi = furi_alloc(sizeof(Irukagotchi)); | ||||||
| 
 | 
 | ||||||
|     irukagotchi->icon = assets_icons_get(A_Tamagotchi_14); |     irukagotchi->icon = assets_icons_get(I_Flipper_young_80x60); | ||||||
|     icon_start_animation(irukagotchi->icon); |     icon_start_animation(irukagotchi->icon); | ||||||
| 
 | 
 | ||||||
|     irukagotchi->widget = widget_alloc(); |     irukagotchi->widget = widget_alloc(); | ||||||
|  | |||||||
| @ -9,12 +9,14 @@ | |||||||
| 
 | 
 | ||||||
| #include "menu_event.h" | #include "menu_event.h" | ||||||
| #include "menu_item.h" | #include "menu_item.h" | ||||||
|  | #include <assets_icons.h> | ||||||
| 
 | 
 | ||||||
| struct Menu { | struct Menu { | ||||||
|     MenuEvent* event; |     MenuEvent* event; | ||||||
| 
 | 
 | ||||||
|     // GUI
 |     // GUI
 | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|  |     Icon* icon; | ||||||
| 
 | 
 | ||||||
|     // State
 |     // State
 | ||||||
|     MenuItem* root; |     MenuItem* root; | ||||||
| @ -56,7 +58,8 @@ void menu_build_main(Menu* menu) { | |||||||
|     // Root point
 |     // Root point
 | ||||||
|     menu->root = menu_item_alloc_menu(NULL, NULL); |     menu->root = menu_item_alloc_menu(NULL, NULL); | ||||||
| 
 | 
 | ||||||
|     menu->settings = menu_item_alloc_menu("Setting", NULL); |     Icon* icon = assets_icons_get(A_Bluetooth_14); | ||||||
|  |     menu->settings = menu_item_alloc_menu("Setting", icon); | ||||||
| 
 | 
 | ||||||
|     menu_item_add(menu, menu->settings); |     menu_item_add(menu, menu->settings); | ||||||
| } | } | ||||||
| @ -103,16 +106,18 @@ void menu_widget_callback(CanvasApi* canvas, void* context) { | |||||||
|         canvas->set_font(canvas, FontPrimary); |         canvas->set_font(canvas, FontPrimary); | ||||||
|         shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items)); |         shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items)); | ||||||
|         item = *MenuItemArray_get(*items, shift_position); |         item = *MenuItemArray_get(*items, shift_position); | ||||||
|         canvas->draw_icon(canvas, 4, 24, menu_item_get_icon(item)); |         canvas->draw_icon(canvas, 4, 25, menu_item_get_icon(item)); | ||||||
|         canvas->draw_str(canvas, 22, 35, menu_item_get_label(item)); |         canvas->draw_str(canvas, 22, 36, menu_item_get_label(item)); | ||||||
|         // Third line
 |         // Third line
 | ||||||
|         canvas->set_font(canvas, FontSecondary); |         canvas->set_font(canvas, FontSecondary); | ||||||
|         shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items)); |         shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items)); | ||||||
|         item = *MenuItemArray_get(*items, shift_position); |         item = *MenuItemArray_get(*items, shift_position); | ||||||
|         canvas->draw_icon(canvas, 4, 46, menu_item_get_icon(item)); |         canvas->draw_icon(canvas, 4, 47, menu_item_get_icon(item)); | ||||||
|         canvas->draw_str(canvas, 22, 57, menu_item_get_label(item)); |         canvas->draw_str(canvas, 22, 58, menu_item_get_label(item)); | ||||||
|         // Frame and scrollbar
 |         // Frame and scrollbar
 | ||||||
|         elements_frame(canvas, 0, 20, 128 - 5, 22); |         // elements_frame(canvas, 0, 0, 128 - 5, 21);
 | ||||||
|  |         elements_frame(canvas, 0, 21, 128 - 5, 21); | ||||||
|  |         // elements_frame(canvas, 0, 42, 128 - 5, 21);
 | ||||||
|         elements_scrollbar(canvas, position, items_count); |         elements_scrollbar(canvas, position, items_count); | ||||||
|     } else { |     } else { | ||||||
|         canvas->draw_str(canvas, 2, 32, "Empty"); |         canvas->draw_str(canvas, 2, 32, "Empty"); | ||||||
| @ -122,9 +127,33 @@ void menu_widget_callback(CanvasApi* canvas, void* context) { | |||||||
|     release_mutex((ValueMutex*)context, menu); |     release_mutex((ValueMutex*)context, menu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void menu_set_icon(Menu* menu, Icon* icon) { | ||||||
|  |     assert(menu); | ||||||
|  | 
 | ||||||
|  |     if(menu->icon) { | ||||||
|  |         icon_stop_animation(menu->icon); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     menu->icon = icon; | ||||||
|  | 
 | ||||||
|  |     if(menu->icon) { | ||||||
|  |         icon_start_animation(menu->icon); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void menu_update(Menu* menu) { | void menu_update(Menu* menu) { | ||||||
|     furi_assert(menu); |     furi_assert(menu); | ||||||
| 
 | 
 | ||||||
|  |     if(menu->current) { | ||||||
|  |         size_t position = menu_item_get_position(menu->current); | ||||||
|  |         MenuItemArray_t* items = menu_item_get_subitems(menu->current); | ||||||
|  |         size_t items_count = MenuItemArray_size(*items); | ||||||
|  |         if(items_count) { | ||||||
|  |             MenuItem* item = *MenuItemArray_get(*items, position); | ||||||
|  |             menu_set_icon(menu, menu_item_get_icon(item)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     menu_event_activity_notify(menu->event); |     menu_event_activity_notify(menu->event); | ||||||
|     widget_update(menu->widget); |     widget_update(menu->widget); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										117
									
								
								applications/power/power.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								applications/power/power.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | |||||||
|  | #include "power.h" | ||||||
|  | 
 | ||||||
|  | #include <flipper_v2.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/widget.h> | ||||||
|  | #include <assets_icons.h> | ||||||
|  | 
 | ||||||
|  | #define BATTERY_MIN_VOLTAGE 3.2f | ||||||
|  | #define BATTERY_MAX_VOLTAGE 4.0f | ||||||
|  | #define BATTERY_INIT 0xFFAACCEE | ||||||
|  | 
 | ||||||
|  | extern ADC_HandleTypeDef hadc1; | ||||||
|  | 
 | ||||||
|  | struct Power { | ||||||
|  |     Icon* usb_icon; | ||||||
|  |     Widget* usb_widget; | ||||||
|  | 
 | ||||||
|  |     Icon* battery_icon; | ||||||
|  |     Widget* battery_widget; | ||||||
|  | 
 | ||||||
|  |     uint32_t charge; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void power_draw_usb_callback(CanvasApi* canvas, void* context) { | ||||||
|  |     assert(context); | ||||||
|  |     Power* power = context; | ||||||
|  |     canvas->draw_icon(canvas, 0, 0, power->usb_icon); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_draw_battery_callback(CanvasApi* canvas, void* context) { | ||||||
|  |     assert(context); | ||||||
|  |     Power* power = context; | ||||||
|  | 
 | ||||||
|  |     canvas->draw_icon(canvas, 0, 0, power->battery_icon); | ||||||
|  | 
 | ||||||
|  |     if(power->charge != BATTERY_INIT) { | ||||||
|  |         float charge = ((float)power->charge / 1000 * 2 - BATTERY_MIN_VOLTAGE) / | ||||||
|  |                        (BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE); | ||||||
|  |         if(charge > 1) { | ||||||
|  |             charge = 1; | ||||||
|  |         } | ||||||
|  |         canvas->draw_box(canvas, 2, 2, charge * 14, 4); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_input_events_callback(const void* value, void* ctx) { | ||||||
|  |     assert(ctx); | ||||||
|  |     Power* power = ctx; | ||||||
|  |     InputEvent* event = value; | ||||||
|  | 
 | ||||||
|  |     if(event->input != InputCharging) return; | ||||||
|  | 
 | ||||||
|  |     widget_enabled_set(power->usb_widget, event->state); | ||||||
|  |     widget_update(power->usb_widget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Power* power_alloc() { | ||||||
|  |     Power* power = furi_alloc(sizeof(Power)); | ||||||
|  | 
 | ||||||
|  |     power->usb_icon = assets_icons_get(I_USBConnected_15x8); | ||||||
|  |     power->usb_widget = widget_alloc(); | ||||||
|  |     widget_set_width(power->usb_widget, icon_get_width(power->usb_icon)); | ||||||
|  | 
 | ||||||
|  |     ValueManager* input_state_manager = furi_open("input_state"); | ||||||
|  |     InputState input_state; | ||||||
|  |     read_mutex_block(input_state_manager, &input_state, sizeof(input_state)); | ||||||
|  |     widget_enabled_set(power->usb_widget, input_state.charging); | ||||||
|  | 
 | ||||||
|  |     widget_draw_callback_set(power->usb_widget, power_draw_usb_callback, power); | ||||||
|  | 
 | ||||||
|  |     power->battery_icon = assets_icons_get(I_Battery_19x8); | ||||||
|  |     power->battery_widget = widget_alloc(); | ||||||
|  |     widget_set_width(power->battery_widget, icon_get_width(power->battery_icon)); | ||||||
|  |     widget_draw_callback_set(power->battery_widget, power_draw_battery_callback, power); | ||||||
|  | 
 | ||||||
|  |     PubSub* input_event_record = furi_open("input_events"); | ||||||
|  |     assert(input_event_record); | ||||||
|  |     subscribe_pubsub(input_event_record, power_input_events_callback, power); | ||||||
|  | 
 | ||||||
|  |     power->charge = BATTERY_INIT; | ||||||
|  | 
 | ||||||
|  |     return power; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_free(Power* power) { | ||||||
|  |     assert(power); | ||||||
|  |     free(power); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_task(void* p) { | ||||||
|  |     (void)p; | ||||||
|  |     Power* power = power_alloc(); | ||||||
|  | 
 | ||||||
|  |     FuriRecordSubscriber* gui_record = furi_open_deprecated("gui", false, false, NULL, NULL, NULL); | ||||||
|  |     assert(gui_record); | ||||||
|  |     GuiApi* gui = furi_take(gui_record); | ||||||
|  |     assert(gui); | ||||||
|  |     gui->add_widget(gui, power->usb_widget, GuiLayerStatusBarLeft); | ||||||
|  |     gui->add_widget(gui, power->battery_widget, GuiLayerStatusBarRight); | ||||||
|  |     furi_commit(gui_record); | ||||||
|  | 
 | ||||||
|  |     if(!furi_create("power", power)) { | ||||||
|  |         printf("[power_task] unable to create power record\n"); | ||||||
|  |         furiac_exit(NULL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furiac_ready(); | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         HAL_ADC_Start(&hadc1); | ||||||
|  |         if(HAL_ADC_PollForConversion(&hadc1, 1000) != HAL_TIMEOUT) { | ||||||
|  |             power->charge = HAL_ADC_GetValue(&hadc1); | ||||||
|  |             widget_update(power->battery_widget); | ||||||
|  |         } | ||||||
|  |         osDelay(1000); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								applications/power/power.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								applications/power/power.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef struct Power Power; | ||||||
							
								
								
									
										3
									
								
								assets/icons/IrukaGotchi/Flipper_idle_76x52.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/IrukaGotchi/Flipper_idle_76x52.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | version https://git-lfs.github.com/spec/v1 | ||||||
|  | oid sha256:526c2637560aab102f259c029eeee93cc13e5cd8bdb935acb624f1318502f286 | ||||||
|  | size 652 | ||||||
							
								
								
									
										3
									
								
								assets/icons/IrukaGotchi/Flipper_young_80x60.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/IrukaGotchi/Flipper_young_80x60.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | version https://git-lfs.github.com/spec/v1 | ||||||
|  | oid sha256:c7d2c5da9b957635189c2ee475d7065ae714fd6fb5d6f0c83ca5fa0bcd1497f8 | ||||||
|  | size 2653 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく