Assets, Icons, UI toolkit. Part 1. (#202)
* Assets: import from gdrive * Assets: fully working build pipeline and registry * NFC: menu icon * Gui: layers. Irukagotchi: base app to handle input on idle screen. Makefile: reboot after flash in dfu mode. * ValueMutex: with lambda * Assets: proper animation frames sorting * ValueMutex: proper furi_open usage * Assets,GUI: split icon into icon data and icon state. * Format source code * Docker: add python3 and imagemagic * Irukagotchi: cleanup * GUI: event lock moved to gui. * Rfid: proper gui layer * GUI: elements. Menu: frame and scroll * GUI, Menu: format code. * Menu: dual font main menu * Menu: format sources * GUI: 0 total scrollbar * CI: enable lfs * Menu: pixel perfect main menu Co-authored-by: Aleksandr Kutuzov <aku@plooks.com> Co-authored-by: aanper <mail@s3f.ru>
This commit is contained in:
		
							parent
							
								
									e699c4a31e
								
							
						
					
					
						commit
						665f6d7d9c
					
				
							
								
								
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @ -16,6 +16,7 @@ jobs: | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|             submodules: true | ||||
|             lfs: true | ||||
| 
 | ||||
|       - uses: satackey/action-docker-layer-caching@v0.0.8 | ||||
|         continue-on-error: true | ||||
|  | ||||
| @ -74,7 +74,7 @@ void app_loader(void* p) { | ||||
|         printf("gui is not available\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
|     gui->add_widget(gui, state.widget, WidgetLayerFullscreen); | ||||
|     gui->add_widget(gui, state.widget, GuiLayerFullscreen); | ||||
| 
 | ||||
|     { | ||||
|         Menu* menu = acquire_mutex_block(menu_mutex); | ||||
|  | ||||
| @ -30,6 +30,7 @@ void app_loader(void* p); | ||||
| void cc1101_workaround(void* p); | ||||
| void lf_rfid_workaround(void* p); | ||||
| void nfc_task(void* p); | ||||
| void irukagotchi_task(void* p); | ||||
| 
 | ||||
| const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
| #ifdef APP_DISPLAY | ||||
| @ -54,6 +55,10 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
|     {.app = app_loader, .name = "app_loader", .libs = {1, FURI_LIB{"menu_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_IRUKAGOTCHI | ||||
|     {.app = irukagotchi_task, .name = "irukagotchi_task", .libs = {1, FURI_LIB{"menu_task"}}}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_CC1101 | ||||
|     {.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}}, | ||||
| #endif | ||||
|  | ||||
| @ -11,6 +11,7 @@ ifeq ($(APP_RELEASE), 1) | ||||
| APP_MENU = 1 | ||||
| APP_NFC  = 1 | ||||
| BUILD_IRDA  = 1 | ||||
| APP_IRUKAGOTCHI = 1 | ||||
| BUILD_EXAMPLE_BLINK = 1 | ||||
| BUILD_EXAMPLE_UART_WRITE = 1 | ||||
| BUILD_EXAMPLE_INPUT_DUMP = 1 | ||||
| @ -26,6 +27,13 @@ CFLAGS		+= -DAPP_NFC | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/nfc/*.c) | ||||
| endif | ||||
| 
 | ||||
| APP_IRUKAGOTCHI ?= 0 | ||||
| ifeq ($(APP_IRUKAGOTCHI), 1) | ||||
| APP_MENU	= 1 | ||||
| CFLAGS		+= -DAPP_IRUKAGOTCHI | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/irukagotchi/*.c) | ||||
| endif | ||||
| 
 | ||||
| APP_MENU ?= 0 | ||||
| ifeq ($(APP_MENU), 1) | ||||
| CFLAGS += -DAPP_MENU | ||||
|  | ||||
| @ -229,7 +229,7 @@ extern "C" void cc1101_workaround(void* p) { | ||||
|         printf("[cc1101] gui is not available\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
|     gui->add_widget(gui, widget, WidgetLayerFullscreen); | ||||
|     gui->add_widget(gui, widget, GuiLayerFullscreen); | ||||
| 
 | ||||
|     printf("[cc1101] creating device\n"); | ||||
|     GpioPin cs_pin = {CC1101_CS_GPIO_Port, CC1101_CS_Pin}; | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| #include "canvas.h" | ||||
| #include "canvas_i.h" | ||||
| #include "icon.h" | ||||
| #include "icon_i.h" | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <flipper.h> | ||||
| @ -20,6 +22,11 @@ void canvas_clear(CanvasApi* api); | ||||
| void canvas_color_set(CanvasApi* api, uint8_t color); | ||||
| void canvas_font_set(CanvasApi* api, Font font); | ||||
| void canvas_str_draw(CanvasApi* api, uint8_t x, uint8_t y, const char* str); | ||||
| 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_box_draw(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); | ||||
| void canvas_draw_line(CanvasApi* api, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); | ||||
| 
 | ||||
| uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); | ||||
| uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); | ||||
| @ -43,6 +50,11 @@ CanvasApi* canvas_api_init() { | ||||
|     canvas->api.set_color = canvas_color_set; | ||||
|     canvas->api.set_font = canvas_font_set; | ||||
|     canvas->api.draw_str = canvas_str_draw; | ||||
|     canvas->api.draw_icon = canvas_icon_draw; | ||||
|     canvas->api.draw_dot = canvas_dot_draw; | ||||
|     canvas->api.draw_box = canvas_box_draw; | ||||
|     canvas->api.draw_frame = canvas_draw_frame; | ||||
|     canvas->api.draw_line = canvas_draw_line; | ||||
| 
 | ||||
|     return (CanvasApi*)canvas; | ||||
| } | ||||
| @ -112,8 +124,43 @@ void canvas_font_set(CanvasApi* api, Font font) { | ||||
| 
 | ||||
| void canvas_str_draw(CanvasApi* api, uint8_t x, uint8_t y, const char* str) { | ||||
|     assert(api); | ||||
|     if(!str) return; | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     x += canvas->offset_x; | ||||
|     y += canvas->offset_y; | ||||
|     u8g2_DrawStr(&canvas->fb, x, y, str); | ||||
| } | ||||
| 
 | ||||
| void canvas_icon_draw(CanvasApi* api, uint8_t x, uint8_t y, Icon* icon) { | ||||
|     assert(api); | ||||
|     if(!icon) return; | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     x += canvas->offset_x; | ||||
|     y += canvas->offset_y; | ||||
|     u8g2_DrawXBM( | ||||
|         &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_get_data(icon)); | ||||
| } | ||||
| 
 | ||||
| void canvas_dot_draw(CanvasApi* api, uint8_t x, uint8_t y) { | ||||
|     assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     u8g2_DrawPixel(&canvas->fb, x, y); | ||||
| } | ||||
| 
 | ||||
| void canvas_box_draw(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | ||||
|     assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     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) { | ||||
|     assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     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) { | ||||
|     assert(api); | ||||
|     Canvas* canvas = (Canvas*)api; | ||||
|     u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2); | ||||
| } | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <u8g2.h> | ||||
| #include <gui/icon.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     ColorWhite = 0x00, | ||||
| @ -24,4 +25,9 @@ struct CanvasApi { | ||||
|     void (*set_font)(CanvasApi* canvas, Font font); | ||||
| 
 | ||||
|     void (*draw_str)(CanvasApi* canvas, uint8_t x, uint8_t y, const char* str); | ||||
|     void (*draw_icon)(CanvasApi* canvas, uint8_t x, uint8_t y, Icon* icon); | ||||
|     void (*draw_dot)(CanvasApi* canvas, uint8_t x, uint8_t y); | ||||
|     void (*draw_box)(CanvasApi* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||
|     void (*draw_frame)(CanvasApi* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||
|     void (*draw_line)(CanvasApi* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); | ||||
| }; | ||||
|  | ||||
							
								
								
									
										31
									
								
								applications/gui/elements.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								applications/gui/elements.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| #include "elements.h" | ||||
| 
 | ||||
| void elements_scrollbar(CanvasApi* canvas, uint8_t pos, uint8_t total) { | ||||
|     uint8_t width = canvas->width(canvas); | ||||
|     uint8_t height = canvas->height(canvas); | ||||
|     // prevent overflows
 | ||||
|     canvas->set_color(canvas, ColorWhite); | ||||
|     canvas->draw_box(canvas, width - 3, 0, 3, height); | ||||
|     // dot line
 | ||||
|     canvas->set_color(canvas, ColorBlack); | ||||
|     for(uint8_t i = 0; i < height; i += 2) { | ||||
|         canvas->draw_dot(canvas, width - 2, i); | ||||
|     } | ||||
|     // Position block
 | ||||
|     if(total) { | ||||
|         uint8_t block_h = ((float)height) / total; | ||||
|         canvas->draw_box(canvas, width - 3, block_h * pos, 3, block_h); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void elements_frame(CanvasApi* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | ||||
|     canvas->draw_line(canvas, x + 2, y, x + width - 2, y); | ||||
|     canvas->draw_line(canvas, x + 1, y + height - 1, x + width, y + height - 1); | ||||
|     canvas->draw_line(canvas, x + 2, y + height, x + width - 1, y + height); | ||||
| 
 | ||||
|     canvas->draw_line(canvas, x, y + 2, x, y + height - 2); | ||||
|     canvas->draw_line(canvas, x + width - 1, y + 1, x + width - 1, y + height - 2); | ||||
|     canvas->draw_line(canvas, x + width, y + 2, x + width, y + height - 2); | ||||
| 
 | ||||
|     canvas->draw_dot(canvas, x + 1, y + 1); | ||||
| } | ||||
							
								
								
									
										8
									
								
								applications/gui/elements.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								applications/gui/elements.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include "canvas.h" | ||||
| 
 | ||||
| void elements_scrollbar(CanvasApi* canvas, uint8_t pos, uint8_t total); | ||||
| 
 | ||||
| void elements_frame(CanvasApi* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||
| @ -3,8 +3,8 @@ | ||||
| 
 | ||||
| #include <flipper.h> | ||||
| #include <flipper_v2.h> | ||||
| #include <stdio.h> | ||||
| #include <m-array.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "gui_event.h" | ||||
| #include "canvas.h" | ||||
| @ -18,54 +18,10 @@ struct Gui { | ||||
|     GuiApi api; | ||||
|     GuiEvent* event; | ||||
|     CanvasApi* canvas_api; | ||||
|     WidgetArray_t widgets_status_bar; | ||||
|     WidgetArray_t widgets; | ||||
|     WidgetArray_t widgets_fs; | ||||
|     WidgetArray_t widgets_dialog; | ||||
|     WidgetArray_t layers[GuiLayerMAX]; | ||||
|     osMutexId_t mutex; | ||||
| }; | ||||
| 
 | ||||
| void gui_add_widget(GuiApi* gui_api, Widget* widget, WidgetLayer layer) { | ||||
|     assert(gui_api); | ||||
|     assert(widget); | ||||
|     Gui* gui = (Gui*)gui_api; | ||||
| 
 | ||||
|     // TODO add mutex on widget array
 | ||||
|     WidgetArray_t* widget_array = NULL; | ||||
| 
 | ||||
|     switch(layer) { | ||||
|     case WidgetLayerStatusBar: | ||||
|         widget_array = &gui->widgets_status_bar; | ||||
|         break; | ||||
|     case WidgetLayerMain: | ||||
|         widget_array = &gui->widgets; | ||||
|         break; | ||||
|     case WidgetLayerFullscreen: | ||||
|         widget_array = &gui->widgets_fs; | ||||
|         break; | ||||
|     case WidgetLayerDialog: | ||||
|         widget_array = &gui->widgets_dialog; | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     assert(widget_array); | ||||
| 
 | ||||
|     gui_event_lock(gui->event); | ||||
|     WidgetArray_push_back(*widget_array, widget); | ||||
|     widget_gui_set(widget, gui); | ||||
|     gui_event_unlock(gui->event); | ||||
| 
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_update(Gui* gui) { | ||||
|     assert(gui); | ||||
|     GuiMessage message; | ||||
|     message.type = GuiMessageTypeRedraw; | ||||
|     gui_event_messsage_send(gui->event, &message); | ||||
| } | ||||
| 
 | ||||
| Widget* gui_widget_find_enabled(WidgetArray_t array) { | ||||
|     size_t widgets_count = WidgetArray_size(array); | ||||
|     for(size_t i = 0; i < widgets_count; i++) { | ||||
| @ -77,9 +33,16 @@ Widget* gui_widget_find_enabled(WidgetArray_t array) { | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void gui_update(Gui* gui) { | ||||
|     assert(gui); | ||||
|     GuiMessage message; | ||||
|     message.type = GuiMessageTypeRedraw; | ||||
|     gui_event_messsage_send(gui->event, &message); | ||||
| } | ||||
| 
 | ||||
| bool gui_redraw_fs(Gui* gui) { | ||||
|     canvas_frame_set(gui->canvas_api, 0, 0, 128, 64); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->widgets_fs); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
|         return true; | ||||
| @ -88,64 +51,128 @@ bool gui_redraw_fs(Gui* gui) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void gui_redraw_status_bar(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->widgets_status_bar); | ||||
|     if(widget) widget_draw(widget, gui->canvas_api); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerStatusBar]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void gui_redraw_normal(Gui* gui) { | ||||
| bool gui_redraw_normal(Gui* gui) { | ||||
|     canvas_frame_set(gui->canvas_api, 0, 9, 128, 55); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->widgets); | ||||
|     if(widget) widget_draw(widget, gui->canvas_api); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerMain]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void gui_redraw_dialogs(Gui* gui) { | ||||
|     canvas_frame_set(gui->canvas_api, 10, 20, 118, 44); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->widgets_dialog); | ||||
|     if(widget) widget_draw(widget, gui->canvas_api); | ||||
| bool gui_redraw_none(Gui* gui) { | ||||
|     canvas_frame_set(gui->canvas_api, 0, 9, 118, 44); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerNone]); | ||||
|     if(widget) { | ||||
|         widget_draw(widget, gui->canvas_api); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void gui_redraw(Gui* gui) { | ||||
|     assert(gui); | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     if(!gui_redraw_fs(gui)) { | ||||
|         if(!gui_redraw_normal(gui)) { | ||||
|             gui_redraw_none(gui); | ||||
|         } | ||||
|         gui_redraw_status_bar(gui); | ||||
|         gui_redraw_normal(gui); | ||||
|     } | ||||
|     gui_redraw_dialogs(gui); | ||||
| 
 | ||||
|     canvas_commit(gui->canvas_api); | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_input(Gui* gui, InputEvent* input_event) { | ||||
|     assert(gui); | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     Widget* widget = gui_widget_find_enabled(gui->widgets_dialog); | ||||
|     if(!widget) widget = gui_widget_find_enabled(gui->widgets_fs); | ||||
|     if(!widget) widget = gui_widget_find_enabled(gui->widgets); | ||||
|     Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||
|     if(!widget) widget = gui_widget_find_enabled(gui->layers[GuiLayerMain]); | ||||
|     if(!widget) widget = gui_widget_find_enabled(gui->layers[GuiLayerNone]); | ||||
| 
 | ||||
|     if(widget) { | ||||
|         widget_input(widget, input_event); | ||||
|     } | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_lock(Gui* gui) { | ||||
|     assert(gui); | ||||
|     assert(osMutexAcquire(gui->mutex, osWaitForever) == osOK); | ||||
| } | ||||
| 
 | ||||
| void gui_unlock(Gui* gui) { | ||||
|     assert(gui); | ||||
|     assert(osMutexRelease(gui->mutex) == osOK); | ||||
| } | ||||
| 
 | ||||
| void gui_add_widget(GuiApi* gui_api, Widget* widget, GuiLayer layer) { | ||||
|     assert(gui_api); | ||||
|     assert(widget); | ||||
|     assert(layer < GuiLayerMAX); | ||||
|     Gui* gui = (Gui*)gui_api; | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
|     WidgetArray_push_back(gui->layers[layer], widget); | ||||
|     widget_gui_set(widget, gui); | ||||
|     gui_unlock(gui); | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_remove_widget(GuiApi* gui_api, Widget* widget) { | ||||
|     assert(gui_api); | ||||
|     assert(widget); | ||||
|     Gui* gui = (Gui*)gui_api; | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     widget_gui_set(widget, NULL); | ||||
|     WidgetArray_it_t it; | ||||
|     for(size_t i = 0; i < GuiLayerMAX; i++) { | ||||
|         WidgetArray_it(it, gui->layers[i]); | ||||
|         while(!WidgetArray_end_p(it)) { | ||||
|             if(*WidgetArray_ref(it) == widget) { | ||||
|                 WidgetArray_remove(gui->layers[i], it); | ||||
|             } | ||||
|             WidgetArray_next(it); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
| Gui* gui_alloc() { | ||||
|     Gui* gui = furi_alloc(sizeof(Gui)); | ||||
| 
 | ||||
|     // Initialize widget arrays
 | ||||
|     WidgetArray_init(gui->widgets_status_bar); | ||||
|     WidgetArray_init(gui->widgets); | ||||
|     WidgetArray_init(gui->widgets_fs); | ||||
|     WidgetArray_init(gui->widgets_dialog); | ||||
| 
 | ||||
|     // Set API functions
 | ||||
|     gui->api.add_widget = gui_add_widget; | ||||
|     gui->api.remove_widget = gui_remove_widget; | ||||
|     // Allocate mutex
 | ||||
|     gui->mutex = osMutexNew(NULL); | ||||
|     assert(gui->mutex); | ||||
|     // Event dispatcher
 | ||||
|     gui->event = gui_event_alloc(); | ||||
| 
 | ||||
|     // Drawing canvas api
 | ||||
|     gui->canvas_api = canvas_api_init(); | ||||
| 
 | ||||
|     gui->api.add_widget = gui_add_widget; | ||||
|     // Compose Layers
 | ||||
|     for(size_t i = 0; i < GuiLayerMAX; i++) { | ||||
|         WidgetArray_init(gui->layers[i]); | ||||
|     } | ||||
| 
 | ||||
|     return gui; | ||||
| } | ||||
|  | ||||
| @ -4,15 +4,17 @@ | ||||
| #include "canvas.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     WidgetLayerStatusBar, | ||||
|     WidgetLayerMain, | ||||
|     WidgetLayerFullscreen, | ||||
|     WidgetLayerDialog | ||||
| } WidgetLayer; | ||||
|     GuiLayerNone, /* Special layer for internal use only */ | ||||
| 
 | ||||
| typedef struct Widget Widget; | ||||
|     GuiLayerStatusBar, /* Status bar widget layer */ | ||||
|     GuiLayerMain, /* Main widget layer, status bar is shown */ | ||||
|     GuiLayerFullscreen, /* Fullscreen widget layer */ | ||||
| 
 | ||||
|     GuiLayerMAX /* Don't use or move, special value */ | ||||
| } GuiLayer; | ||||
| 
 | ||||
| typedef struct GuiApi GuiApi; | ||||
| struct GuiApi { | ||||
|     void (*add_widget)(GuiApi* gui_api, Widget* widget, WidgetLayer layer); | ||||
|     void (*add_widget)(GuiApi* gui_api, Widget* widget, GuiLayer layer); | ||||
|     void (*remove_widget)(GuiApi* gui_api, Widget* widget); | ||||
| }; | ||||
|  | ||||
| @ -8,7 +8,6 @@ | ||||
| struct GuiEvent { | ||||
|     PubSub* input_event_record; | ||||
|     osMessageQueueId_t mqueue; | ||||
|     osMutexId_t lock_mutex; | ||||
| }; | ||||
| 
 | ||||
| void gui_event_input_events_callback(const void* value, void* ctx) { | ||||
| @ -33,37 +32,15 @@ GuiEvent* gui_event_alloc() { | ||||
|     assert(gui_event->input_event_record != NULL); | ||||
|     subscribe_pubsub(gui_event->input_event_record, gui_event_input_events_callback, gui_event); | ||||
| 
 | ||||
|     // Lock mutex
 | ||||
|     gui_event->lock_mutex = osMutexNew(NULL); | ||||
|     assert(gui_event->lock_mutex); | ||||
|     gui_event_lock(gui_event); | ||||
| 
 | ||||
|     return gui_event; | ||||
| } | ||||
| 
 | ||||
| void gui_event_free(GuiEvent* gui_event) { | ||||
|     osStatus_t status; | ||||
|     assert(gui_event); | ||||
|     gui_event_unlock(gui_event); | ||||
|     status = osMessageQueueDelete(gui_event->mqueue); | ||||
|     assert(status == osOK); | ||||
|     assert(osMessageQueueDelete(gui_event->mqueue) == osOK); | ||||
|     free(gui_event); | ||||
| } | ||||
| 
 | ||||
| void gui_event_lock(GuiEvent* gui_event) { | ||||
|     osStatus_t status; | ||||
|     assert(gui_event); | ||||
|     status = osMutexAcquire(gui_event->lock_mutex, osWaitForever); | ||||
|     assert(status == osOK); | ||||
| } | ||||
| 
 | ||||
| void gui_event_unlock(GuiEvent* gui_event) { | ||||
|     osStatus_t status; | ||||
|     assert(gui_event); | ||||
|     status = osMutexRelease(gui_event->lock_mutex); | ||||
|     assert(status == osOK); | ||||
| } | ||||
| 
 | ||||
| void gui_event_messsage_send(GuiEvent* gui_event, GuiMessage* message) { | ||||
|     assert(gui_event); | ||||
|     assert(message); | ||||
| @ -71,12 +48,10 @@ void gui_event_messsage_send(GuiEvent* gui_event, GuiMessage* message) { | ||||
| } | ||||
| 
 | ||||
| GuiMessage gui_event_message_next(GuiEvent* gui_event) { | ||||
|     osStatus_t status; | ||||
|     assert(gui_event); | ||||
|     GuiMessage message; | ||||
|     gui_event_unlock(gui_event); | ||||
|     status = osMessageQueueGet(gui_event->mqueue, &message, NULL, osWaitForever); | ||||
|     assert(status == osOK); | ||||
|     gui_event_lock(gui_event); | ||||
| 
 | ||||
|     assert(osMessageQueueGet(gui_event->mqueue, &message, NULL, osWaitForever) == osOK); | ||||
| 
 | ||||
|     return message; | ||||
| } | ||||
|  | ||||
| @ -20,10 +20,6 @@ GuiEvent* gui_event_alloc(); | ||||
| 
 | ||||
| void gui_event_free(GuiEvent* gui_event); | ||||
| 
 | ||||
| void gui_event_lock(GuiEvent* gui_event); | ||||
| 
 | ||||
| void gui_event_unlock(GuiEvent* gui_event); | ||||
| 
 | ||||
| void gui_event_messsage_send(GuiEvent* gui_event, GuiMessage* message); | ||||
| 
 | ||||
| GuiMessage gui_event_message_next(GuiEvent* gui_event); | ||||
|  | ||||
| @ -3,3 +3,5 @@ | ||||
| typedef struct Gui Gui; | ||||
| 
 | ||||
| void gui_update(Gui* gui); | ||||
| void gui_lock(Gui* gui); | ||||
| void gui_unlock(Gui* gui); | ||||
							
								
								
									
										63
									
								
								applications/gui/icon.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								applications/gui/icon.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| #include "icon.h" | ||||
| #include "icon_i.h" | ||||
| 
 | ||||
| #include <cmsis_os2.h> | ||||
| #include <flipper.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| Icon* icon_alloc(const IconData* data) { | ||||
|     Icon* icon = furi_alloc(sizeof(Icon)); | ||||
|     icon->data = data; | ||||
|     return icon; | ||||
| } | ||||
| 
 | ||||
| void icon_free(Icon* icon) { | ||||
|     assert(icon); | ||||
|     free(icon); | ||||
| } | ||||
| 
 | ||||
| const uint8_t* icon_get_data(Icon* icon) { | ||||
|     assert(icon); | ||||
|     if(icon->tick) { | ||||
|         uint32_t now = osKernelGetTickCount(); | ||||
|         if(now < icon->tick) { | ||||
|             icon->tick = now; | ||||
|             icon_next_frame(icon); | ||||
|         } else if(now - icon->tick > osKernelGetTickFreq() / icon->data->frame_rate) { | ||||
|             icon->tick = now; | ||||
|             icon_next_frame(icon); | ||||
|         } | ||||
|     } | ||||
|     return icon->data->frames[icon->frame]; | ||||
| } | ||||
| 
 | ||||
| void icon_next_frame(Icon* icon) { | ||||
|     assert(icon); | ||||
|     icon->frame = (icon->frame + 1) % icon->data->frame_count; | ||||
| } | ||||
| 
 | ||||
| uint8_t icon_get_width(Icon* icon) { | ||||
|     assert(icon); | ||||
|     return icon->data->width; | ||||
| } | ||||
| 
 | ||||
| uint8_t icon_get_height(Icon* icon) { | ||||
|     assert(icon); | ||||
|     return icon->data->height; | ||||
| } | ||||
| 
 | ||||
| bool icon_is_animated(Icon* icon) { | ||||
|     assert(icon); | ||||
|     return icon->data->frame_count > 1; | ||||
| } | ||||
| 
 | ||||
| void icon_start_animation(Icon* icon) { | ||||
|     assert(icon); | ||||
|     icon->tick = osKernelGetTickCount(); | ||||
| } | ||||
| 
 | ||||
| void icon_stop_animation(Icon* icon) { | ||||
|     assert(icon); | ||||
|     icon->tick = 0; | ||||
|     icon->frame = 0; | ||||
| } | ||||
							
								
								
									
										21
									
								
								applications/gui/icon.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								applications/gui/icon.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| typedef struct IconData IconData; | ||||
| typedef struct Icon Icon; | ||||
| 
 | ||||
| Icon* icon_alloc(const IconData* data); | ||||
| 
 | ||||
| void icon_free(Icon* icon); | ||||
| 
 | ||||
| uint8_t icon_get_width(Icon* icon); | ||||
| 
 | ||||
| uint8_t icon_get_height(Icon* icon); | ||||
| 
 | ||||
| bool icon_is_animated(Icon* icon); | ||||
| 
 | ||||
| void icon_start_animation(Icon* icon); | ||||
| 
 | ||||
| void icon_stop_animation(Icon* icon); | ||||
							
								
								
									
										21
									
								
								applications/gui/icon_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								applications/gui/icon_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| struct IconData { | ||||
|     const uint8_t width; | ||||
|     const uint8_t height; | ||||
|     const uint8_t frame_count; | ||||
|     const uint8_t frame_rate; | ||||
|     const uint8_t** frames; | ||||
| }; | ||||
| 
 | ||||
| struct Icon { | ||||
|     const IconData* data; | ||||
|     uint8_t frame; | ||||
|     uint32_t tick; | ||||
| }; | ||||
| 
 | ||||
| const uint8_t* icon_get_data(Icon* icon); | ||||
| 
 | ||||
| void icon_next_frame(Icon* icon); | ||||
| @ -32,8 +32,10 @@ void widget_free(Widget* widget) { | ||||
| 
 | ||||
| void widget_enabled_set(Widget* widget, bool enabled) { | ||||
|     assert(widget); | ||||
|     widget->is_enabled = enabled; | ||||
|     widget_update(widget); | ||||
|     if(widget->is_enabled != enabled) { | ||||
|         widget->is_enabled = enabled; | ||||
|         widget_update(widget); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool widget_is_enabled(Widget* widget) { | ||||
|  | ||||
| @ -262,7 +262,7 @@ void irda(void* p) { | ||||
|         printf("gui is not available\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
|     gui->add_widget(gui, widget, WidgetLayerFullscreen); | ||||
|     gui->add_widget(gui, widget, GuiLayerFullscreen); | ||||
| 
 | ||||
|     AppEvent event; | ||||
|     while(1) { | ||||
|  | ||||
							
								
								
									
										69
									
								
								applications/irukagotchi/irukagotchi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								applications/irukagotchi/irukagotchi.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| #include "irukagotchi.h" | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <flipper_v2.h> | ||||
| 
 | ||||
| #include <gui/gui.h> | ||||
| #include <gui/widget.h> | ||||
| #include <gui/canvas.h> | ||||
| #include <menu/menu.h> | ||||
| 
 | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| struct Irukagotchi { | ||||
|     Icon* icon; | ||||
|     Widget* widget; | ||||
|     ValueMutex* menu_vm; | ||||
| }; | ||||
| 
 | ||||
| void irukagotchi_draw_callback(CanvasApi* canvas, void* context) { | ||||
|     Irukagotchi* irukagotchi = 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"); | ||||
| } | ||||
| 
 | ||||
| void irukagotchi_input_callback(InputEvent* event, void* context) { | ||||
|     Irukagotchi* irukagotchi = context; | ||||
| 
 | ||||
|     if(!event->state || event->input != InputOk) return; | ||||
| 
 | ||||
|     with_value_mutex( | ||||
|         irukagotchi->menu_vm, (Menu * menu) { menu_ok(menu); }); | ||||
| } | ||||
| 
 | ||||
| Irukagotchi* irukagotchi_alloc() { | ||||
|     Irukagotchi* irukagotchi = furi_alloc(sizeof(Irukagotchi)); | ||||
| 
 | ||||
|     irukagotchi->icon = assets_icons_get(A_Tamagotchi_14); | ||||
|     icon_start_animation(irukagotchi->icon); | ||||
| 
 | ||||
|     irukagotchi->widget = widget_alloc(); | ||||
|     widget_draw_callback_set(irukagotchi->widget, irukagotchi_draw_callback, irukagotchi); | ||||
|     widget_input_callback_set(irukagotchi->widget, irukagotchi_input_callback, irukagotchi); | ||||
| 
 | ||||
|     irukagotchi->menu_vm = furi_open("menu"); | ||||
|     assert(irukagotchi->menu_vm); | ||||
| 
 | ||||
|     return irukagotchi; | ||||
| } | ||||
| 
 | ||||
| void irukagotchi_task() { | ||||
|     Irukagotchi* irukagotchi = irukagotchi_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, irukagotchi->widget, GuiLayerNone); | ||||
|     furi_commit(gui_record); | ||||
| 
 | ||||
|     furiac_ready(); | ||||
| 
 | ||||
|     while(1) { | ||||
|         osDelay(osWaitForever); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										3
									
								
								applications/irukagotchi/irukagotchi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								applications/irukagotchi/irukagotchi.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef struct Irukagotchi Irukagotchi; | ||||
| @ -69,7 +69,7 @@ void lf_rfid_workaround(void* p) { | ||||
|         printf("gui is not available\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
|     gui->add_widget(gui, widget, WidgetLayerFullscreen); | ||||
|     gui->add_widget(gui, widget, GuiLayerFullscreen); | ||||
| 
 | ||||
|     AppEvent event; | ||||
|     while(1) { | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| 
 | ||||
| #include <flipper_v2.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "menu_event.h" | ||||
| #include "menu_item.h" | ||||
| @ -19,7 +20,6 @@ struct Menu { | ||||
|     MenuItem* root; | ||||
|     MenuItem* settings; | ||||
|     MenuItem* current; | ||||
|     uint32_t position; | ||||
| }; | ||||
| 
 | ||||
| void menu_widget_callback(CanvasApi* canvas, void* context); | ||||
| @ -42,8 +42,9 @@ ValueMutex* menu_init() { | ||||
|     // Open GUI and register fullscreen widget
 | ||||
|     GuiApi* gui = furi_open("gui"); | ||||
|     assert(gui); | ||||
|     gui->add_widget(gui, menu->widget, WidgetLayerFullscreen); | ||||
|     gui->add_widget(gui, menu->widget, GuiLayerFullscreen); | ||||
| 
 | ||||
|     widget_enabled_set(menu->widget, false); | ||||
|     widget_draw_callback_set(menu->widget, menu_widget_callback, menu_mutex); | ||||
|     widget_input_callback_set(menu->widget, menu_event_input_callback, menu->event); | ||||
| 
 | ||||
| @ -68,6 +69,12 @@ void menu_settings_item_add(Menu* menu, MenuItem* item) { | ||||
|     menu_item_subitem_add(menu->settings, item); | ||||
| } | ||||
| 
 | ||||
| void menu_draw_primary(Menu* menu, CanvasApi* canvas) { | ||||
| } | ||||
| 
 | ||||
| void menu_draw_secondary(Menu* menu, CanvasApi* canvas) { | ||||
| } | ||||
| 
 | ||||
| void menu_widget_callback(CanvasApi* canvas, void* context) { | ||||
|     assert(canvas); | ||||
|     assert(context); | ||||
| @ -75,27 +82,41 @@ void menu_widget_callback(CanvasApi* canvas, void* context) { | ||||
|     Menu* menu = acquire_mutex((ValueMutex*)context, 100); // wait 10 ms to get mutex
 | ||||
|     if(menu == NULL) return; // redraw fail
 | ||||
| 
 | ||||
|     if(!menu->current) { | ||||
|         canvas->clear(canvas); | ||||
|         canvas->set_color(canvas, ColorBlack); | ||||
|         canvas->set_font(canvas, FontPrimary); | ||||
|         canvas->draw_str(canvas, 2, 32, "Idle Screen"); | ||||
|     } else { | ||||
|         MenuItemArray_t* items = menu_item_get_subitems(menu->current); | ||||
|         canvas->clear(canvas); | ||||
|         canvas->set_color(canvas, ColorBlack); | ||||
|         canvas->set_font(canvas, FontSecondary); | ||||
|     assert(menu->current); | ||||
| 
 | ||||
|         if(MenuItemArray_size(*items)) { | ||||
|             for(size_t i = 0; i < 5; i++) { | ||||
|                 size_t shift_position = i + menu->position + MenuItemArray_size(*items) - 2; | ||||
|                 shift_position = shift_position % (MenuItemArray_size(*items)); | ||||
|                 MenuItem* item = *MenuItemArray_get(*items, shift_position); | ||||
|                 canvas->draw_str(canvas, 2, 12 * (i + 1), menu_item_get_label(item)); | ||||
|             } | ||||
|         } else { | ||||
|             canvas->draw_str(canvas, 2, 32, "Empty"); | ||||
|         } | ||||
|     canvas->clear(canvas); | ||||
|     canvas->set_color(canvas, ColorBlack); | ||||
| 
 | ||||
|     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; | ||||
|         size_t shift_position; | ||||
|         // First line
 | ||||
|         canvas->set_font(canvas, FontSecondary); | ||||
|         shift_position = (0 + position + items_count - 1) % (MenuItemArray_size(*items)); | ||||
|         item = *MenuItemArray_get(*items, shift_position); | ||||
|         canvas->draw_icon(canvas, 4, 3, menu_item_get_icon(item)); | ||||
|         canvas->draw_str(canvas, 22, 14, menu_item_get_label(item)); | ||||
|         // Second line main
 | ||||
|         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)); | ||||
|         // 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)); | ||||
|         // Frame and scrollbar
 | ||||
|         elements_frame(canvas, 0, 20, 128 - 5, 22); | ||||
|         elements_scrollbar(canvas, position, items_count); | ||||
|     } else { | ||||
|         canvas->draw_str(canvas, 2, 32, "Empty"); | ||||
|         elements_scrollbar(canvas, 0, 0); | ||||
|     } | ||||
| 
 | ||||
|     release_mutex((ValueMutex*)context, menu); | ||||
| @ -111,18 +132,21 @@ void menu_update(Menu* menu) { | ||||
| void menu_up(Menu* menu) { | ||||
|     assert(menu); | ||||
| 
 | ||||
|     size_t position = menu_item_get_position(menu->current); | ||||
|     MenuItemArray_t* items = menu_item_get_subitems(menu->current); | ||||
|     if(menu->position == 0) menu->position = MenuItemArray_size(*items); | ||||
|     menu->position--; | ||||
|     if(position == 0) position = MenuItemArray_size(*items); | ||||
|     position--; | ||||
|     menu_item_set_position(menu->current, position); | ||||
|     menu_update(menu); | ||||
| } | ||||
| 
 | ||||
| void menu_down(Menu* menu) { | ||||
|     assert(menu); | ||||
| 
 | ||||
|     size_t position = menu_item_get_position(menu->current); | ||||
|     MenuItemArray_t* items = menu_item_get_subitems(menu->current); | ||||
|     menu->position++; | ||||
|     menu->position = menu->position % MenuItemArray_size(*items); | ||||
|     position++; | ||||
|     position = position % MenuItemArray_size(*items); | ||||
|     menu_item_set_position(menu->current, position); | ||||
|     menu_update(menu); | ||||
| } | ||||
| 
 | ||||
| @ -130,18 +154,25 @@ void menu_ok(Menu* menu) { | ||||
|     assert(menu); | ||||
| 
 | ||||
|     if(!menu->current) { | ||||
|         widget_enabled_set(menu->widget, true); | ||||
|         menu->current = menu->root; | ||||
|         menu_item_set_position(menu->current, 0); | ||||
|         menu_update(menu); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     MenuItemArray_t* items = menu_item_get_subitems(menu->current); | ||||
|     MenuItem* item = *MenuItemArray_get(*items, menu->position); | ||||
|     if(!items || MenuItemArray_size(*items) == 0) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     size_t position = menu_item_get_position(menu->current); | ||||
|     MenuItem* item = *MenuItemArray_get(*items, position); | ||||
|     MenuItemType type = menu_item_get_type(item); | ||||
| 
 | ||||
|     if(type == MenuItemTypeMenu) { | ||||
|         menu->current = item; | ||||
|         menu->position = 0; | ||||
|         menu_item_set_position(menu->current, 0); | ||||
|         menu_update(menu); | ||||
|     } else if(type == MenuItemTypeFunction) { | ||||
|         menu_item_function_call(item); | ||||
| @ -153,7 +184,6 @@ void menu_back(Menu* menu) { | ||||
|     MenuItem* parent = menu_item_get_parent(menu->current); | ||||
|     if(parent) { | ||||
|         menu->current = parent; | ||||
|         menu->position = 0; | ||||
|         menu_update(menu); | ||||
|     } else { | ||||
|         menu_exit(menu); | ||||
| @ -162,7 +192,7 @@ void menu_back(Menu* menu) { | ||||
| 
 | ||||
| void menu_exit(Menu* menu) { | ||||
|     assert(menu); | ||||
|     menu->position = 0; | ||||
|     widget_enabled_set(menu->widget, false); | ||||
|     menu->current = NULL; | ||||
|     menu_update(menu); | ||||
| } | ||||
|  | ||||
| @ -6,10 +6,15 @@ | ||||
| 
 | ||||
| struct MenuItem { | ||||
|     MenuItemType type; | ||||
| 
 | ||||
|     const char* label; | ||||
|     void* icon; | ||||
|     Icon* icon; | ||||
| 
 | ||||
|     size_t position; | ||||
|     MenuItem* parent; | ||||
|     void* data; | ||||
| 
 | ||||
|     // callback related
 | ||||
|     MenuItemCallback callback; | ||||
|     void* callback_context; | ||||
| }; | ||||
| @ -19,7 +24,7 @@ MenuItem* menu_item_alloc() { | ||||
|     return menu_item; | ||||
| } | ||||
| 
 | ||||
| MenuItem* menu_item_alloc_menu(const char* label, void* icon) { | ||||
| MenuItem* menu_item_alloc_menu(const char* label, Icon* icon) { | ||||
|     MenuItem* menu_item = menu_item_alloc(); | ||||
| 
 | ||||
|     menu_item->type = MenuItemTypeMenu; | ||||
| @ -34,7 +39,7 @@ MenuItem* menu_item_alloc_menu(const char* label, void* icon) { | ||||
| } | ||||
| 
 | ||||
| MenuItem* | ||||
| menu_item_alloc_function(const char* label, void* icon, MenuItemCallback callback, void* context) { | ||||
| menu_item_alloc_function(const char* label, Icon* icon, MenuItemCallback callback, void* context) { | ||||
|     MenuItem* menu_item = menu_item_alloc(); | ||||
| 
 | ||||
|     menu_item->type = MenuItemTypeFunction; | ||||
| @ -73,6 +78,16 @@ uint8_t menu_item_get_type(MenuItem* menu_item) { | ||||
|     return menu_item->type; | ||||
| } | ||||
| 
 | ||||
| void menu_item_set_position(MenuItem* menu_item, size_t position) { | ||||
|     assert(menu_item); | ||||
|     menu_item->position = position; | ||||
| } | ||||
| 
 | ||||
| size_t menu_item_get_position(MenuItem* menu_item) { | ||||
|     assert(menu_item); | ||||
|     return menu_item->position; | ||||
| } | ||||
| 
 | ||||
| void menu_item_set_label(MenuItem* menu_item, const char* label) { | ||||
|     assert(menu_item); | ||||
|     menu_item->label = label; | ||||
| @ -83,12 +98,12 @@ const char* menu_item_get_label(MenuItem* menu_item) { | ||||
|     return menu_item->label; | ||||
| } | ||||
| 
 | ||||
| void menu_item_set_icon(MenuItem* menu_item, void* icon) { | ||||
| void menu_item_set_icon(MenuItem* menu_item, Icon* icon) { | ||||
|     assert(menu_item); | ||||
|     menu_item->icon = icon; | ||||
| } | ||||
| 
 | ||||
| void* menu_item_get_icon(MenuItem* menu_item) { | ||||
| Icon* menu_item_get_icon(MenuItem* menu_item) { | ||||
|     assert(menu_item); | ||||
|     return menu_item->icon; | ||||
| } | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <m-array.h> | ||||
| #include <gui/icon.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     MenuItemTypeMenu = 0x00, | ||||
| @ -13,10 +14,10 @@ typedef void (*MenuItemCallback)(void* context); | ||||
| 
 | ||||
| ARRAY_DEF(MenuItemArray, MenuItem*, M_PTR_OPLIST); | ||||
| 
 | ||||
| MenuItem* menu_item_alloc_menu(const char* label, void* icon); | ||||
| MenuItem* menu_item_alloc_menu(const char* label, Icon* icon); | ||||
| 
 | ||||
| MenuItem* | ||||
| menu_item_alloc_function(const char* label, void* icon, MenuItemCallback callback, void* context); | ||||
| menu_item_alloc_function(const char* label, Icon* icon, MenuItemCallback callback, void* context); | ||||
| 
 | ||||
| void menu_item_release(MenuItem* menu_item); | ||||
| 
 | ||||
| @ -26,11 +27,14 @@ void menu_item_subitem_add(MenuItem* menu_item, MenuItem* sub_item); | ||||
| 
 | ||||
| MenuItemType menu_item_get_type(MenuItem* menu_item); | ||||
| 
 | ||||
| void menu_item_set_position(MenuItem* menu_item, size_t position); | ||||
| size_t menu_item_get_position(MenuItem* menu_item); | ||||
| 
 | ||||
| void menu_item_set_label(MenuItem* menu_item, const char* label); | ||||
| const char* menu_item_get_label(MenuItem* menu_item); | ||||
| 
 | ||||
| void menu_item_set_icon(MenuItem* menu_item, void* icon); | ||||
| void* menu_item_get_icon(MenuItem* menu_item); | ||||
| void menu_item_set_icon(MenuItem* menu_item, Icon* icon); | ||||
| Icon* menu_item_get_icon(MenuItem* menu_item); | ||||
| 
 | ||||
| MenuItemArray_t* menu_item_get_subitems(MenuItem* menu_item); | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| #include <gui/gui.h> | ||||
| #include <gui/widget.h> | ||||
| #include <gui/canvas.h> | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| #include <menu/menu.h> | ||||
| #include <menu/menu_item.h> | ||||
| @ -28,10 +29,10 @@ typedef struct { | ||||
| 
 | ||||
| struct Nfc { | ||||
|     Dispatcher* dispatcher; | ||||
|     Icon* icon; | ||||
|     Widget* widget; | ||||
|     ValueMutex* menu_vm; | ||||
|     MenuItem* menu; | ||||
|     FuriRecordSubscriber* gui_record; | ||||
|     FuriRecordSubscriber* menu_record; | ||||
|     rfalNfcDiscoverParam* disParams; | ||||
| 
 | ||||
|     osThreadAttr_t worker_attr; | ||||
| @ -222,15 +223,18 @@ void nfc_bridge_callback(void* context) { | ||||
| 
 | ||||
| Nfc* nfc_alloc() { | ||||
|     Nfc* nfc = furi_alloc(sizeof(Nfc)); | ||||
|     assert(nfc); | ||||
| 
 | ||||
|     nfc->dispatcher = dispatcher_alloc(32, sizeof(NfcMessage)); | ||||
| 
 | ||||
|     nfc->icon = assets_icons_get(A_NFC_14); | ||||
|     nfc->widget = widget_alloc(); | ||||
|     widget_draw_callback_set(nfc->widget, nfc_draw_callback, nfc); | ||||
|     widget_input_callback_set(nfc->widget, nfc_input_callback, nfc); | ||||
| 
 | ||||
|     nfc->menu = menu_item_alloc_menu("NFC", NULL); | ||||
|     nfc->menu_vm = furi_open("menu"); | ||||
|     assert(nfc->menu_vm); | ||||
| 
 | ||||
|     nfc->menu = menu_item_alloc_menu("NFC", nfc->icon); | ||||
|     menu_item_subitem_add( | ||||
|         nfc->menu, menu_item_alloc_function("Test", NULL, nfc_test_callback, nfc)); | ||||
|     menu_item_subitem_add( | ||||
| @ -240,12 +244,6 @@ Nfc* nfc_alloc() { | ||||
|     menu_item_subitem_add( | ||||
|         nfc->menu, menu_item_alloc_function("Brdige", NULL, nfc_bridge_callback, nfc)); | ||||
| 
 | ||||
|     nfc->gui_record = furi_open_deprecated("gui", false, false, NULL, NULL, NULL); | ||||
|     assert(nfc->gui_record); | ||||
| 
 | ||||
|     nfc->menu_record = furi_open_deprecated("menu", false, false, NULL, NULL, NULL); | ||||
|     assert(nfc->menu_record); | ||||
| 
 | ||||
|     nfc->worker_attr.name = "nfc_worker"; | ||||
|     // nfc->worker_attr.attr_bits = osThreadJoinable;
 | ||||
|     nfc->worker_attr.stack_size = 4096; | ||||
| @ -255,29 +253,27 @@ Nfc* nfc_alloc() { | ||||
| void nfc_task(void* p) { | ||||
|     Nfc* nfc = nfc_alloc(); | ||||
| 
 | ||||
|     GuiApi* gui = furi_take(nfc->gui_record); | ||||
|     FuriRecordSubscriber* gui_record = furi_open_deprecated("gui", false, false, NULL, NULL, NULL); | ||||
|     assert(gui_record); | ||||
|     GuiApi* gui = furi_take(gui_record); | ||||
|     assert(gui); | ||||
|     widget_enabled_set(nfc->widget, false); | ||||
|     gui->add_widget(gui, nfc->widget, WidgetLayerFullscreen); | ||||
|     furi_commit(nfc->gui_record); | ||||
|     gui->add_widget(gui, nfc->widget, GuiLayerFullscreen); | ||||
|     furi_commit(gui_record); | ||||
| 
 | ||||
|     ValueMutex* menu_mutex = furi_open("menu"); | ||||
|     assert(menu_mutex); | ||||
| 
 | ||||
|     Menu* menu = acquire_mutex_block(menu_mutex); | ||||
|     menu_item_add(menu, nfc->menu); | ||||
|     release_mutex(menu_mutex, menu); | ||||
|     with_value_mutex( | ||||
|         nfc->menu_vm, (Menu * menu) { menu_item_add(menu, nfc->menu); }); | ||||
| 
 | ||||
|     if(!furi_create("nfc", nfc)) { | ||||
|         printf("[nfc_task] cannot create nfc record\n"); | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
|     furiac_ready(); | ||||
| 
 | ||||
|     nfc->ret = rfalNfcInitialize(); | ||||
|     rfalLowPowerModeStart(); | ||||
| 
 | ||||
|     furiac_ready(); | ||||
| 
 | ||||
|     NfcMessage message; | ||||
|     while(1) { | ||||
|         dispatcher_recieve(nfc->dispatcher, (Message*)&message); | ||||
|  | ||||
							
								
								
									
										1
									
								
								assets/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| /headers | ||||
							
								
								
									
										17
									
								
								assets/ReadMe.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								assets/ReadMe.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| # Asset naming rules | ||||
| 
 | ||||
| ## Images and Animations | ||||
| 
 | ||||
| `NAME_VARIANT_SIZE` | ||||
| 
 | ||||
| - `NAME`    - mandatory - Asset name in CamelCase. [A-Za-z0-9], special symbols not allowed | ||||
| - `VARIANT` - optional  - icon variant: can relate to state or rendering conditions. Examples: active, inactive, inverted. | ||||
| - `SIZE`    - mandatory - size in px. Example squere 10, 20, 24, etc. Example rectangular: 10x8, 19x5, etc. | ||||
| 
 | ||||
| Image names will be automatically prefixed with `I_`, animation names with `A_`. | ||||
| Icons and Animations will be gathered into `icon.h` and `icon.c`. | ||||
| 
 | ||||
| # Important notes | ||||
| 
 | ||||
| Don't include assets that you are not using, compiller is not going to strip unusued assets. | ||||
| 
 | ||||
							
								
								
									
										10
									
								
								assets/assets.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								assets/assets.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| ASSETS_DIR			:= $(PROJECT_ROOT)/assets | ||||
| ASSETS_COMPILLER	:= $(ASSETS_DIR)/assets.py | ||||
| ASSETS_OUTPUT_DIR	:= $(ASSETS_DIR)/output | ||||
| ASSETS_SOURCE_DIR	:= $(ASSETS_DIR)/icons | ||||
| 
 | ||||
| ASSETS_SOURCES		+= $(shell find $(ASSETS_SOURCE_DIR) -type f -iname '*.png' -or -iname 'frame_rate') | ||||
| ASSETS				+= $(ASSETS_OUTPUT_DIR)/assets_icons.c | ||||
| 
 | ||||
| CFLAGS				+= -I$(ASSETS_OUTPUT_DIR) | ||||
| C_SOURCES			+= $(ASSETS_OUTPUT_DIR)/assets_icons.c | ||||
							
								
								
									
										184
									
								
								assets/assets.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										184
									
								
								assets/assets.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,184 @@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| import logging | ||||
| import argparse | ||||
| import subprocess | ||||
| import io | ||||
| import os | ||||
| import sys | ||||
| 
 | ||||
| ICONS_SUPPORTED_FORMATS = ["png"] | ||||
| 
 | ||||
| ICONS_TEMPLATE_H_HEADER = """#pragma once | ||||
| 
 | ||||
| #include <gui/icon.h> | ||||
| 
 | ||||
| typedef enum { | ||||
| """ | ||||
| ICONS_TEMPLATE_H_ICON_NAME = "\t{name},\n" | ||||
| ICONS_TEMPLATE_H_FOOTER = """} IconName; | ||||
| 
 | ||||
| Icon * assets_icons_get(IconName name); | ||||
| """ | ||||
| 
 | ||||
| ICONS_TEMPLATE_C_HEADER = """#include \"assets_icons.h\" | ||||
| #include <gui/icon_i.h> | ||||
| 
 | ||||
| """ | ||||
| ICONS_TEMPLATE_C_FRAME = "const uint8_t {name}[] = {data};\n" | ||||
| ICONS_TEMPLATE_C_DATA = "const uint8_t *{name}[] = {data};\n" | ||||
| ICONS_TEMPLATE_C_ICONS_ARRAY_START = "const IconData icons[] = {\n" | ||||
| ICONS_TEMPLATE_C_ICONS_ITEM = "\t{{ .width={width}, .height={height}, .frame_count={frame_count}, .frame_rate={frame_rate}, .frames=_{name} }},\n" | ||||
| ICONS_TEMPLATE_C_ICONS_ARRAY_END = "};" | ||||
| ICONS_TEMPLATE_C_FOOTER = """ | ||||
| 
 | ||||
| Icon * assets_icons_get(IconName name) { | ||||
|     return icon_alloc(&icons[name]); | ||||
| } | ||||
| """ | ||||
| 
 | ||||
| 
 | ||||
| class Assets: | ||||
|     def __init__(self): | ||||
|         # command args | ||||
|         self.parser = argparse.ArgumentParser() | ||||
|         self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") | ||||
|         self.subparsers = self.parser.add_subparsers(help="sub-command help") | ||||
|         self.parser_icons = self.subparsers.add_parser( | ||||
|             "icons", help="Process icons and build icon registry" | ||||
|         ) | ||||
|         self.parser_icons.add_argument( | ||||
|             "-s", "--source-directory", help="Source directory" | ||||
|         ) | ||||
|         self.parser_icons.add_argument( | ||||
|             "-o", "--output-directory", help="Output directory" | ||||
|         ) | ||||
|         self.parser_icons.set_defaults(func=self.icons) | ||||
|         # logging | ||||
|         self.logger = logging.getLogger() | ||||
| 
 | ||||
|     def __call__(self): | ||||
|         self.args = self.parser.parse_args() | ||||
|         if "func" not in self.args: | ||||
|             self.parser.error("Choose something to do") | ||||
|         # configure log output | ||||
|         self.log_level = logging.DEBUG if self.args.debug else logging.INFO | ||||
|         self.logger.setLevel(self.log_level) | ||||
|         self.handler = logging.StreamHandler(sys.stdout) | ||||
|         self.handler.setLevel(self.log_level) | ||||
|         self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") | ||||
|         self.handler.setFormatter(self.formatter) | ||||
|         self.logger.addHandler(self.handler) | ||||
|         # execute requested function | ||||
|         self.args.func() | ||||
| 
 | ||||
|     def icons(self): | ||||
|         self.logger.debug(f"Converting icons") | ||||
|         icons_c = open(os.path.join(self.args.output_directory, "assets_icons.c"), "w") | ||||
|         icons_c.write(ICONS_TEMPLATE_C_HEADER) | ||||
|         icons = [] | ||||
|         # Traverse icons tree, append image data to source file | ||||
|         for dirpath, dirnames, filenames in os.walk(self.args.source_directory): | ||||
|             self.logger.debug(f"Processing directory {dirpath}") | ||||
|             if not filenames: | ||||
|                 continue | ||||
|             if "frame_rate" in filenames: | ||||
|                 self.logger.debug(f"Folder contatins animation") | ||||
|                 icon_name = "A_" + os.path.split(dirpath)[1].replace("-", "_") | ||||
|                 width = height = None | ||||
|                 frame_count = 0 | ||||
|                 frame_rate = 0 | ||||
|                 frame_names = [] | ||||
|                 for filename in sorted(filenames): | ||||
|                     fullfilename = os.path.join(dirpath, filename) | ||||
|                     if filename == "frame_rate": | ||||
|                         frame_rate = int(open(fullfilename, "r").read().strip()) | ||||
|                         continue | ||||
|                     elif not self.iconIsSupported(filename): | ||||
|                         continue | ||||
|                     self.logger.debug(f"Processing animation frame {filename}") | ||||
|                     temp_width, temp_height, data = self.icon2header(fullfilename) | ||||
|                     if width is None: | ||||
|                         width = temp_width | ||||
|                     if height is None: | ||||
|                         height = temp_height | ||||
|                     assert width == temp_width | ||||
|                     assert height == temp_height | ||||
|                     frame_name = f"_{icon_name}_{frame_count}" | ||||
|                     frame_names.append(frame_name) | ||||
|                     icons_c.write( | ||||
|                         ICONS_TEMPLATE_C_FRAME.format(name=frame_name, data=data) | ||||
|                     ) | ||||
|                     frame_count += 1 | ||||
|                 assert frame_rate > 0 | ||||
|                 assert frame_count > 0 | ||||
|                 icons_c.write( | ||||
|                     ICONS_TEMPLATE_C_DATA.format( | ||||
|                         name=f"_{icon_name}", data=f'{{{",".join(frame_names)}}}' | ||||
|                     ) | ||||
|                 ) | ||||
|                 icons_c.write("\n") | ||||
|                 icons.append((icon_name, width, height, frame_rate, frame_count)) | ||||
|             else: | ||||
|                 # process icons | ||||
|                 for filename in filenames: | ||||
|                     if not self.iconIsSupported(filename): | ||||
|                         continue | ||||
|                     self.logger.debug(f"Processing icon {filename}") | ||||
|                     icon_name = "I_" + "_".join(filename.split(".")[:-1]).replace( | ||||
|                         "-", "_" | ||||
|                     ) | ||||
|                     fullfilename = os.path.join(dirpath, filename) | ||||
|                     width, height, data = self.icon2header(fullfilename) | ||||
|                     frame_name = f"_{icon_name}_0" | ||||
|                     icons_c.write( | ||||
|                         ICONS_TEMPLATE_C_FRAME.format(name=frame_name, data=data) | ||||
|                     ) | ||||
|                     icons_c.write( | ||||
|                         ICONS_TEMPLATE_C_DATA.format( | ||||
|                             name=f"_{icon_name}", data=f"{{{frame_name}}}" | ||||
|                         ) | ||||
|                     ) | ||||
|                     icons_c.write("\n") | ||||
|                     icons.append((icon_name, width, height, 0, 1)) | ||||
|         # Create array of images: | ||||
|         self.logger.debug(f"Finalizing source file") | ||||
|         icons_c.write(ICONS_TEMPLATE_C_ICONS_ARRAY_START) | ||||
|         for name, width, height, frame_rate, frame_count in icons: | ||||
|             icons_c.write( | ||||
|                 ICONS_TEMPLATE_C_ICONS_ITEM.format( | ||||
|                     name=name, | ||||
|                     width=width, | ||||
|                     height=height, | ||||
|                     frame_rate=frame_rate, | ||||
|                     frame_count=frame_count, | ||||
|                 ) | ||||
|             ) | ||||
|         icons_c.write(ICONS_TEMPLATE_C_ICONS_ARRAY_END) | ||||
|         icons_c.write(ICONS_TEMPLATE_C_FOOTER) | ||||
|         icons_c.write("\n") | ||||
|         # Create Header | ||||
|         self.logger.debug(f"Creating header") | ||||
|         icons_h = open(os.path.join(self.args.output_directory, "assets_icons.h"), "w") | ||||
|         icons_h.write(ICONS_TEMPLATE_H_HEADER) | ||||
|         for name, width, height, frame_rate, frame_count in icons: | ||||
|             icons_h.write(ICONS_TEMPLATE_H_ICON_NAME.format(name=name)) | ||||
|         icons_h.write(ICONS_TEMPLATE_H_FOOTER) | ||||
|         self.logger.debug(f"Done") | ||||
| 
 | ||||
|     def icon2header(self, file): | ||||
|         output = subprocess.check_output(["convert", file, "xbm:-"]) | ||||
|         assert output | ||||
|         f = io.StringIO(output.decode().strip()) | ||||
|         width = int(f.readline().strip().split(" ")[2]) | ||||
|         height = int(f.readline().strip().split(" ")[2]) | ||||
|         data = f.read().strip().replace("\n", "").replace(" ", "").split("=")[1][:-1] | ||||
|         return width, height, data | ||||
| 
 | ||||
|     def iconIsSupported(self, filename): | ||||
|         extension = filename.lower().split(".")[-1] | ||||
|         return extension in ICONS_SUPPORTED_FORMATS | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     Assets()() | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:e3b8745f1c3cf2b3b5023d40cb2fde21afdb44f4eff3e456e2bd4eafbccb17f1 | ||||
| size 257 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:6d24e445214cfc96ed699ad7f6fc5966e6fef08a7fe36add479e7b02e1458b4c | ||||
| size 257 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:27f19713ab1d7b6113fb2f8a0402c93a763652e6b2c28693a858f26cce1acb6d | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/125khz_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:37271fbd71ebe1561fb4a292504d8692367c588688c534c3b9ce7122439bdca0 | ||||
| size 255 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/125khz_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/125khz_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:470df999ccb281350e21e86f5f21235f23ceed5fd2391d435af3b073403962ad | ||||
| size 252 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:22b29a0bc9a2aa4c5258edd18def520813eefe6a028036b0f95629217565e814 | ||||
| size 251 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:67fd7032e3b6bad02a2d8e58b1f9c3d10a77e3c8ed48b5819047ae35e98b405e | ||||
| size 251 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:969215f7462ad2d4fdf053bf67ee3a912d46debe87815e10554b58399f2c4d20 | ||||
| size 248 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:c1154e6634b065ab40849236a3172284ce1e7dd4fd7212a1ca4bdc5b48a26ffa | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:811a93e5b8770c2578be16c5cedce9663aa8608f7061c29cba949daf94a93b48 | ||||
| size 251 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/Bluetooth_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:dbe853e0056d3b93e7beba725a660a6a8186562c2be20745aea3b2f30bcbbf91 | ||||
| size 254 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:f7076f51c03ad1e5f2f9ab00679b1142a37224caa3a44b3ea272fec94a806cf5 | ||||
| size 255 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:db95faeadfd4aeb9d7b56cb10f67a945d56f6d3c7199d066903d436ef2f0ceaa | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:267da4a3a640c4a75aa2d06ffd057c9ec10308137e0f73378a638ce96c3e9f19 | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:c88f44fcffc7ade9cfb1bcfc9dbd687776211a3d57e8278e75fbf19376d462fa | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:160e99941884fd6ac6ce19830a3b39cdc7197881689b8cbc0b3761be561c1cf7 | ||||
| size 237 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_07.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_07.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:c88f44fcffc7ade9cfb1bcfc9dbd687776211a3d57e8278e75fbf19376d462fa | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_08.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_08.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:267da4a3a640c4a75aa2d06ffd057c9ec10308137e0f73378a638ce96c3e9f19 | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_09.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_09.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:db95faeadfd4aeb9d7b56cb10f67a945d56f6d3c7199d066903d436ef2f0ceaa | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_10.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/FileManager_14/frame_10.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:f7076f51c03ad1e5f2f9ab00679b1142a37224caa3a44b3ea272fec94a806cf5 | ||||
| size 255 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/FileManager_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/FileManager_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:9ab102ccd1b92236bed296eb3cdb4dc088fca6fa7e32e72d752b49e4012ffcff | ||||
| size 242 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:588fabc1c63bd8815aaf61c8b8b6a7e88ef12a210f8ce04ecec6a75c4eaa48dd | ||||
| size 237 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:66d2f7c971c180f7ea27c90aaf1ffeb8f62584c8fbd7c390fcda3165ecd9cdb2 | ||||
| size 236 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:d4a40f59194adb698cac9472a14ac34a7e0b56e6251f157d11982bdff0f88745 | ||||
| size 235 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:4ad47da62b20627fd428ec857cbadb14803d41702b639c72e820aa8759fbf424 | ||||
| size 231 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:ededf42eb58de6082fc9f97abc73b053417f2421c547cd2f3aa9814d0eb985ab | ||||
| size 235 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_07.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_07.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:44ef494e78d60728791a632dedad8a374fcd863e4a1d409c565bd7f11e142e52 | ||||
| size 235 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_08.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/GPIO_14/frame_08.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:05e946f15d71eae155c5a6438cea4f5ad56ef310577fb894ebe5c3b41f9e2e24 | ||||
| size 237 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/GPIO_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/GPIO_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:b101d691964aa96546bb70bb08f638b48c6000230ded6597ac533b0bd36a8c86 | ||||
| size 250 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:5322b1c4366b8931d132292c71318f02e6756536dd38165a1f65dea5c9dfb7b2 | ||||
| size 247 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:da8a0d2055a7a835be42bde6f71ba2d17710ad308201946fb07d97207c32e3c4 | ||||
| size 243 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:04bb17687f4b0572b4ffd7c7220cb0070456b95df9849fb55e620d02fd7fee94 | ||||
| size 235 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:1eafe712679be62faa958b2a648a514a461c08ae846ae942888cf7f22d6c3deb | ||||
| size 235 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Infrared_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:e58c3bbcfec585b2f920b06b7464dba32686571156c59a46265d599222c7331a | ||||
| size 243 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/Infrared_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/Infrared_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:3430783069a7fa3763bc1cbe6d251540128237249ab8268c666ee180715b6d36 | ||||
| size 256 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:95ed4aa304cb3e7de4aa536fb67df8cd5b6413cb67f77924729d31015f298262 | ||||
| size 250 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:2cb767e8560b1b7f25226aa1a0fc2c46625bbf94df77fcdbdb4b14834616e762 | ||||
| size 244 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/NFC_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:00cac0303198a2baa9243b0466e755d98a408587d73c1899532a86aeb75a80c5 | ||||
| size 247 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/NFC_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/NFC_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:227acbe3f7482e1efefc11b0cf8734f01062540db99accb969a521907d58c693 | ||||
| size 249 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:ebe14fca1ff8d5093a05d4d452c9ee55eb7bf48d8b8fc71f98a74476e703d030 | ||||
| size 248 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:7b1b5373cf67c493b77dfcc5821754c8ec0c41eee633f81059e9507ce8102a27 | ||||
| size 242 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:b12e03c09e699a5446bac1ad67bdf34e299b65e49995664e30bc8e14500dd26f | ||||
| size 228 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:9592833f1c810b9939e5a213f448c090f86dbf75bf7721ca85a6c166ebc79a6c | ||||
| size 231 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:950f24058fd92684f1665e280f740ce662e078d03c0e59a546d21d4569f691a8 | ||||
| size 242 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/Sub1ghz_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:68bda2a589f180b9e5231d74eab098cdaedc08ad44d74b37f3a69e6570799c42 | ||||
| size 258 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:dfd4f583da99e73e11abb5acbb1235cf49acf6d61e337ad6c702cba1d8ff5ed6 | ||||
| size 258 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:77ba2d0d27048bdc0d1b8faaef530e019c5ca2a7966c6d2e84bc4206c220a3a4 | ||||
| size 258 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:bcb7b3c96a531cea583ec917a26037bce04e1704c0e96c096d88bd164891d3b8 | ||||
| size 258 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:2ee856a02fb0c0b7e9f59825ce80d008bbdfde10d733db25ae2accac3cae7fb4 | ||||
| size 258 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:159ed78bae7c6ba8b91dbd5fc72475478852c667bdece15f68f318bfdfd7a0fc | ||||
| size 258 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/Tamagotchi_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:11043601935b50e020ea672ed8aa8dddbec56a6925abd2cb6cfedf965c54e73b | ||||
| size 256 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:abd8be6bfec3facb698a2c248f6ef75f0411d4ee75a0fc653959c5d07b4dab5e | ||||
| size 255 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:75000555e5119e163d9b3afd28861e78347acfb1bf2f704e731a26d994edd1ff | ||||
| size 254 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/U2F_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:abd8be6bfec3facb698a2c248f6ef75f0411d4ee75a0fc653959c5d07b4dab5e | ||||
| size 255 | ||||
							
								
								
									
										1
									
								
								assets/icons/MainMenu/U2F_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/icons/MainMenu/U2F_14/frame_rate
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_01.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:22cbd3b5f4c41d2ede7b89d1d4212f3da0b7da815d2bc219f95106d06424f3f2 | ||||
| size 256 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_02.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:6f75d8bb8a4c2c67855053ca7eedf2ce724937b246a3b5a5cf95b8df6122116c | ||||
| size 260 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_03.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:527ebea445642be096f4bd5209af3b7b74c53e688cea5d29de6d40dad31d08ca | ||||
| size 256 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_04.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:ee6e95dbe9aae5a16504d42d94b24f8c3ef44108ddb726c43faec09d3cbedb7e | ||||
| size 260 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_05.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:2b12ea1162db6e239562eff4cc434f4aaf86fe00b5c371805d801780a1e54258 | ||||
| size 252 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_06.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:93c876d79ec21c394233392f6356d24078904a81f46e354ae591757287f074e7 | ||||
| size 255 | ||||
							
								
								
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_07.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/MainMenu/iButton_14/frame_07.png
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:901b67368cd1df6f767200c021e180427830168496fe5dcc48b05bf51fcc0f38 | ||||
| size 262 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく