1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 12:51:22 +04:00

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:
あく
2020-10-26 20:00:17 +03:00
committed by GitHub
parent e699c4a31e
commit 665f6d7d9c
116 changed files with 1057 additions and 184 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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};

View File

@@ -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);
}

View File

@@ -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);
};

View 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);
}

View 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);

View File

@@ -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)) {
gui_redraw_status_bar(gui);
gui_redraw_normal(gui);
if(!gui_redraw_normal(gui)) {
gui_redraw_none(gui);
}
gui_redraw_status_bar(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;
}

View File

@@ -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);
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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
View 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
View 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
View 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);

View File

@@ -32,8 +32,10 @@ void widget_free(Widget* widget) {
void widget_enabled_set(Widget* widget, bool enabled) {
assert(widget);
if(widget->is_enabled != enabled) {
widget->is_enabled = enabled;
widget_update(widget);
}
}
bool widget_is_enabled(Widget* widget) {

View File

@@ -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) {

View 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);
}
}

View File

@@ -0,0 +1,3 @@
#pragma once
typedef struct Irukagotchi Irukagotchi;

View File

@@ -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) {

View File

@@ -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));
}
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);
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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
View File

@@ -0,0 +1 @@
/headers

17
assets/ReadMe.md Normal file
View 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
View 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
View 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()()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More