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 "menu/menu.h" | ||||
| #include "applications.h" | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| typedef struct { | ||||
|     FuriApp* handler; | ||||
| @ -87,7 +88,9 @@ void app_loader(void* p) { | ||||
|             ctx->app = &FLIPPER_APPS[i]; | ||||
| 
 | ||||
|             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 nfc_task(void* p); | ||||
| void irukagotchi_task(void* p); | ||||
| void power_task(void* p); | ||||
| 
 | ||||
| const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
| #ifdef APP_DISPLAY | ||||
| @ -59,6 +60,10 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
|     {.app = irukagotchi_task, .name = "irukagotchi_task", .libs = {1, FURI_LIB{"menu_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_POWER | ||||
|     {.app = power_task, .name = "power_task", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_CC1101 | ||||
|     {.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
|  | ||||
| @ -10,6 +10,7 @@ APP_RELEASE ?= 0 | ||||
| ifeq ($(APP_RELEASE), 1) | ||||
| APP_MENU = 1 | ||||
| APP_NFC  = 1 | ||||
| APP_POWER = 1 | ||||
| BUILD_IRDA  = 1 | ||||
| APP_IRUKAGOTCHI = 1 | ||||
| BUILD_EXAMPLE_BLINK = 1 | ||||
| @ -34,6 +35,13 @@ CFLAGS		+= -DAPP_IRUKAGOTCHI | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/irukagotchi/*.c) | ||||
| 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 | ||||
| ifeq ($(APP_MENU), 1) | ||||
| CFLAGS += -DAPP_MENU | ||||
|  | ||||
| @ -64,6 +64,12 @@ void canvas_api_free(CanvasApi* 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) { | ||||
|     furi_assert(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) { | ||||
|     furi_assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     x += canvas->offset_x; | ||||
|     y += canvas->offset_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) { | ||||
|     furi_assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     x += canvas->offset_x; | ||||
|     y += canvas->offset_y; | ||||
|     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) { | ||||
|     furi_assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     x += canvas->offset_x; | ||||
|     y += canvas->offset_y; | ||||
|     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) { | ||||
|     furi_assert(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); | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,8 @@ CanvasApi* canvas_api_init(); | ||||
| 
 | ||||
| void canvas_api_free(CanvasApi* api); | ||||
| 
 | ||||
| void canvas_reset(CanvasApi* api); | ||||
| 
 | ||||
| void canvas_commit(CanvasApi* api); | ||||
| 
 | ||||
| void canvas_frame_set( | ||||
|  | ||||
| @ -41,7 +41,7 @@ void gui_update(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]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
| @ -51,18 +51,48 @@ bool gui_redraw_fs(Gui* gui) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool gui_redraw_status_bar(Gui* gui) { | ||||
|     canvas_frame_set(gui->canvas_api, 0, 0, 128, 64); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerStatusBar]); | ||||
|     if(widget) { | ||||
| void gui_redraw_status_bar(Gui* gui) { | ||||
|     WidgetArray_it_t it; | ||||
|     uint8_t x; | ||||
|     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); | ||||
|         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) { | ||||
|     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]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
| @ -72,7 +102,7 @@ bool gui_redraw_normal(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]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
| @ -86,6 +116,8 @@ void gui_redraw(Gui* gui) { | ||||
|     furi_assert(gui); | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     canvas_reset(gui->canvas_api); | ||||
| 
 | ||||
|     if(!gui_redraw_fs(gui)) { | ||||
|         if(!gui_redraw_normal(gui)) { | ||||
|             gui_redraw_none(gui); | ||||
|  | ||||
| @ -3,10 +3,24 @@ | ||||
| #include "widget.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 { | ||||
|     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 */ | ||||
|     GuiLayerFullscreen, /* Fullscreen widget layer */ | ||||
| 
 | ||||
|  | ||||
| @ -6,9 +6,20 @@ | ||||
| 
 | ||||
| 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); | ||||
| @ -29,6 +40,10 @@ GuiEvent* gui_event_alloc() { | ||||
|     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, 1000 / 10); | ||||
| 
 | ||||
|     // Input
 | ||||
|     gui_event->input_event_record = furi_open("input_events"); | ||||
|     furi_check(gui_event->input_event_record != NULL); | ||||
|  | ||||
| @ -10,15 +10,6 @@ | ||||
| 
 | ||||
| // 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 = furi_alloc(sizeof(Widget)); | ||||
|     widget->is_enabled = true; | ||||
| @ -31,6 +22,26 @@ void widget_free(Widget* 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) { | ||||
|     furi_assert(widget); | ||||
|     if(widget->is_enabled != enabled) { | ||||
| @ -80,7 +91,9 @@ void widget_draw(Widget* widget, CanvasApi* canvas_api) { | ||||
| void widget_input(Widget* widget, InputEvent* event) { | ||||
|     furi_assert(widget); | ||||
|     furi_assert(event); | ||||
| 
 | ||||
|     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 (*WidgetInputCallback)(InputEvent* event, void* context); | ||||
| 
 | ||||
| /*
 | ||||
|  * Widget allocator | ||||
|  * always returns widget or stops system if not enough memory. | ||||
|  */ | ||||
| Widget* widget_alloc(); | ||||
| 
 | ||||
| /*
 | ||||
|  * Widget deallocator | ||||
|  * Ensure that widget was unregistered in GUI system before use. | ||||
|  */ | ||||
| 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); | ||||
| 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_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); | ||||
|  | ||||
| @ -2,8 +2,31 @@ | ||||
| 
 | ||||
| #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); | ||||
| 
 | ||||
| /*
 | ||||
|  * Process draw call. Calls draw callback. | ||||
|  * @param canvas_api - canvas to draw at. | ||||
|  */ | ||||
| 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); | ||||
|  | ||||
| @ -21,8 +21,9 @@ void irukagotchi_draw_callback(CanvasApi* canvas, void* context) { | ||||
|     canvas->clear(canvas); | ||||
|     canvas->set_color(canvas, ColorBlack); | ||||
|     canvas->set_font(canvas, FontPrimary); | ||||
|     canvas->draw_icon(canvas, 10, 20, irukagotchi->icon); | ||||
|     canvas->draw_str(canvas, 30, 32, "Irukagotchi"); | ||||
|     canvas->draw_icon(canvas, 0, 0, irukagotchi->icon); | ||||
|     canvas->draw_str(canvas, 80, 30, "111001"); | ||||
|     canvas->draw_str(canvas, 80, 42, "011010"); | ||||
| } | ||||
| 
 | ||||
| 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 = 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); | ||||
| 
 | ||||
|     irukagotchi->widget = widget_alloc(); | ||||
|  | ||||
| @ -9,12 +9,14 @@ | ||||
| 
 | ||||
| #include "menu_event.h" | ||||
| #include "menu_item.h" | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| struct Menu { | ||||
|     MenuEvent* event; | ||||
| 
 | ||||
|     // GUI
 | ||||
|     Widget* widget; | ||||
|     Icon* icon; | ||||
| 
 | ||||
|     // State
 | ||||
|     MenuItem* root; | ||||
| @ -56,7 +58,8 @@ void menu_build_main(Menu* menu) { | ||||
|     // Root point
 | ||||
|     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); | ||||
| } | ||||
| @ -103,16 +106,18 @@ void menu_widget_callback(CanvasApi* canvas, void* context) { | ||||
|         canvas->set_font(canvas, FontPrimary); | ||||
|         shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items)); | ||||
|         item = *MenuItemArray_get(*items, shift_position); | ||||
|         canvas->draw_icon(canvas, 4, 24, menu_item_get_icon(item)); | ||||
|         canvas->draw_str(canvas, 22, 35, menu_item_get_label(item)); | ||||
|         canvas->draw_icon(canvas, 4, 25, menu_item_get_icon(item)); | ||||
|         canvas->draw_str(canvas, 22, 36, menu_item_get_label(item)); | ||||
|         // Third line
 | ||||
|         canvas->set_font(canvas, FontSecondary); | ||||
|         shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items)); | ||||
|         item = *MenuItemArray_get(*items, shift_position); | ||||
|         canvas->draw_icon(canvas, 4, 46, menu_item_get_icon(item)); | ||||
|         canvas->draw_str(canvas, 22, 57, menu_item_get_label(item)); | ||||
|         canvas->draw_icon(canvas, 4, 47, menu_item_get_icon(item)); | ||||
|         canvas->draw_str(canvas, 22, 58, menu_item_get_label(item)); | ||||
|         // 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); | ||||
|     } else { | ||||
|         canvas->draw_str(canvas, 2, 32, "Empty"); | ||||
| @ -122,9 +127,33 @@ void menu_widget_callback(CanvasApi* canvas, void* context) { | ||||
|     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) { | ||||
|     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); | ||||
|     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
	 あく
						あく