6 more apps gone
6
applications/external/application.fam
vendored
@@ -1,6 +0,0 @@
|
||||
# Placeholder
|
||||
App(
|
||||
appid="external_apps",
|
||||
name="External apps bundle",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
)
|
||||
19
applications/external/hex_editor/application.fam
vendored
@@ -1,19 +0,0 @@
|
||||
App(
|
||||
appid="hex_editor",
|
||||
name="HEX Editor",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="hex_editor_app",
|
||||
cdefines=["APP_HEX_EDITOR"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="icons/edit_10px.png",
|
||||
fap_category="Tools",
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@dunaevai135",
|
||||
fap_weburl="https://github.com/dunaevai135/flipper-zero-hex_editor",
|
||||
fap_version="1.1",
|
||||
fap_description="Read text files line by line and edit them without a computer or smartphone.",
|
||||
)
|
||||
357
applications/external/hex_editor/hex_editor.c
vendored
@@ -1,357 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <input/input.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <stream/stream.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
|
||||
#include "hex_editor_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "HexEditor"
|
||||
|
||||
typedef struct {
|
||||
// uint8_t file_bytes[HEX_editor_LINES_ON_SCREEN][HEX_editor_BYTES_PER_LINE];
|
||||
uint32_t file_offset;
|
||||
uint32_t file_read_bytes;
|
||||
uint32_t file_size;
|
||||
uint8_t string_offset;
|
||||
char editable_char;
|
||||
Stream* stream;
|
||||
bool mode; // Print address or content
|
||||
} HexEditorModel;
|
||||
|
||||
typedef struct {
|
||||
HexEditorModel* model;
|
||||
FuriMutex** mutex;
|
||||
|
||||
FuriMessageQueue* input_queue;
|
||||
|
||||
ViewPort* view_port;
|
||||
Gui* gui;
|
||||
Storage* storage;
|
||||
|
||||
FuriString* buffer;
|
||||
} HexEditor;
|
||||
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
// UNUSED(ctx);
|
||||
HexEditor* hex_editor = ctx;
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, 10, "Line and mode:");
|
||||
// elements_button_right(canvas, "Info");
|
||||
|
||||
// // elements_string_fit_width(canvas, buffer, 100);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
0,
|
||||
20,
|
||||
AlignLeft,
|
||||
AlignBottom,
|
||||
furi_string_get_cstr(hex_editor->buffer) + hex_editor->model->string_offset);
|
||||
// elements_scrollable_text_line(
|
||||
// canvas, 0, 20, 128, hex_editor->buffer, hex_editor->model->string_offset, false);
|
||||
|
||||
// canvas_draw_line(canvas, 3, 20, 5, 30);
|
||||
|
||||
canvas_draw_icon(canvas, 0, 20, &I_Pin_arrow_up_7x9);
|
||||
|
||||
if(hex_editor->model->mode) {
|
||||
elements_button_left(canvas, "ASCII -");
|
||||
elements_button_right(canvas, "ASCII +");
|
||||
} else {
|
||||
elements_button_left(canvas, "");
|
||||
elements_button_right(canvas, "");
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_glyph(canvas, 0, 45, '0' + hex_editor->model->mode);
|
||||
canvas_draw_glyph(canvas, 30, 45, hex_editor->model->editable_char);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
// Проверяем, что контекст не нулевой
|
||||
furi_assert(ctx);
|
||||
HexEditor* hex_editor = ctx;
|
||||
|
||||
furi_message_queue_put(hex_editor->input_queue, input_event, 100);
|
||||
}
|
||||
|
||||
static HexEditor* hex_editor_alloc() {
|
||||
HexEditor* instance = malloc(sizeof(HexEditor));
|
||||
|
||||
instance->model = malloc(sizeof(HexEditorModel));
|
||||
memset(instance->model, 0x0, sizeof(HexEditorModel));
|
||||
|
||||
instance->model->editable_char = ' ';
|
||||
|
||||
instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
instance->view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(instance->view_port, draw_callback, instance);
|
||||
view_port_input_callback_set(instance->view_port, input_callback, instance);
|
||||
|
||||
instance->gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
|
||||
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
instance->buffer = furi_string_alloc();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void hex_editor_free(HexEditor* instance) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
gui_remove_view_port(instance->gui, instance->view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_port_free(instance->view_port);
|
||||
|
||||
furi_message_queue_free(instance->input_queue);
|
||||
|
||||
furi_mutex_free(instance->mutex);
|
||||
|
||||
if(instance->model->stream) buffered_file_stream_close(instance->model->stream);
|
||||
|
||||
furi_string_free(instance->buffer);
|
||||
|
||||
free(instance->model);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool hex_editor_open_file(HexEditor* hex_editor, const char* file_path) {
|
||||
furi_assert(hex_editor);
|
||||
furi_assert(file_path);
|
||||
|
||||
hex_editor->model->stream = buffered_file_stream_alloc(hex_editor->storage);
|
||||
bool isOk = true;
|
||||
|
||||
do {
|
||||
if(!buffered_file_stream_open(
|
||||
hex_editor->model->stream, file_path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_E(TAG, "Unable to open stream: %s", file_path);
|
||||
isOk = false;
|
||||
break;
|
||||
};
|
||||
|
||||
hex_editor->model->file_size = stream_size(hex_editor->model->stream);
|
||||
} while(false);
|
||||
|
||||
return isOk;
|
||||
}
|
||||
|
||||
// static bool hex_editor_read_file(HexEditor* hex_editor) {
|
||||
// furi_assert(hex_editor);
|
||||
// furi_assert(hex_editor->model->stream);
|
||||
// // furi_assert(hex_editor->model->file_offset % hex_editor_BYTES_PER_LINE == 0);
|
||||
|
||||
// memset(hex_editor->model->file_bytes, 0x0, hex_editor_BUF_SIZE);
|
||||
// bool isOk = true;
|
||||
|
||||
// do {
|
||||
// uint32_t offset = hex_editor->model->file_offset;
|
||||
// if(!stream_seek(hex_editor->model->stream, offset, true)) {
|
||||
// FURI_LOG_E(TAG, "Unable to seek stream");
|
||||
// isOk = false;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// hex_editor->model->file_read_bytes = stream_read(
|
||||
// hex_editor->model->stream,
|
||||
// (uint8_t*)hex_editor->model->file_bytes,
|
||||
// hex_editor_BUF_SIZE);
|
||||
// } while(false);
|
||||
|
||||
// return isOk;
|
||||
// }
|
||||
|
||||
int32_t hex_editor_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
HexEditor* hex_editor = hex_editor_alloc();
|
||||
|
||||
FuriString* file_path;
|
||||
file_path = furi_string_alloc();
|
||||
|
||||
// furi_string_printf(
|
||||
// hex_editor->buffer,
|
||||
// "qqqqq1231231232343454565676urtfgsdfascesc\nasdqwe\new ra sssssssssssssssssssssssssqqqqqqqqqqq1231231232343454565676urtfgsdfascesc\nq2e");
|
||||
|
||||
do {
|
||||
if(p && strlen(p)) {
|
||||
furi_string_set(file_path, (const char*)p);
|
||||
} else {
|
||||
furi_string_set(file_path, STORAGE_EXT_PATH_PREFIX);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, "*", &I_edit_10px);
|
||||
browser_options.hide_ext = false;
|
||||
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
|
||||
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
if(!res) {
|
||||
FURI_LOG_I(TAG, "No file selected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path));
|
||||
|
||||
if(!hex_editor_open_file(hex_editor, furi_string_get_cstr(file_path))) break;
|
||||
|
||||
if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) {
|
||||
FURI_LOG_T(TAG, "No keys left in dict");
|
||||
break;
|
||||
}
|
||||
|
||||
InputEvent event;
|
||||
int8_t off;
|
||||
while(1) {
|
||||
// Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста)
|
||||
// и проверяем, что у нас получилось это сделать
|
||||
furi_check(
|
||||
furi_message_queue_get(hex_editor->input_queue, &event, FuriWaitForever) ==
|
||||
FuriStatusOk);
|
||||
|
||||
// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
|
||||
if(event.type == InputTypeShort || event.type == InputTypeRepeat) {
|
||||
if(!hex_editor->model->mode) {
|
||||
off = 1;
|
||||
if(event.type == InputTypeRepeat) {
|
||||
off = 2;
|
||||
}
|
||||
if(event.key == InputKeyRight) {
|
||||
hex_editor->model->string_offset += off;
|
||||
if(hex_editor->model->string_offset >=
|
||||
furi_string_size(hex_editor->buffer)) {
|
||||
// dengeros
|
||||
hex_editor->model->string_offset -=
|
||||
furi_string_size(hex_editor->buffer);
|
||||
}
|
||||
}
|
||||
if(event.key == InputKeyLeft) {
|
||||
if(hex_editor->model->string_offset - off < 0) {
|
||||
// dengeros
|
||||
hex_editor->model->string_offset +=
|
||||
furi_string_size(hex_editor->buffer);
|
||||
}
|
||||
hex_editor->model->string_offset -= off;
|
||||
}
|
||||
if(event.key == InputKeyDown) {
|
||||
hex_editor->model->string_offset = 0;
|
||||
if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) {
|
||||
FURI_LOG_T(TAG, "No keys left in dict");
|
||||
}
|
||||
}
|
||||
if(event.key == InputKeyUp) {
|
||||
hex_editor->model->string_offset = 0;
|
||||
// TODO asert
|
||||
if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) {
|
||||
FURI_LOG_E(TAG, "Unable to seek stream");
|
||||
break;
|
||||
}
|
||||
// NOT work on first line
|
||||
stream_seek_to_char(
|
||||
hex_editor->model->stream, '\n', StreamDirectionBackward);
|
||||
|
||||
// if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) {
|
||||
// FURI_LOG_E(TAG, "Unable to seek stream");
|
||||
// break;
|
||||
// }
|
||||
if(!stream_seek_to_char(
|
||||
hex_editor->model->stream, '\n', StreamDirectionBackward)) {
|
||||
stream_rewind(hex_editor->model->stream);
|
||||
} else {
|
||||
if(!stream_seek(
|
||||
hex_editor->model->stream, 1, StreamOffsetFromCurrent)) {
|
||||
FURI_LOG_E(TAG, "Unable to seek stream");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) {
|
||||
FURI_LOG_T(TAG, "No keys left in dict");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(event.key == InputKeyOk) {
|
||||
hex_editor->model->editable_char = furi_string_get_char(
|
||||
hex_editor->buffer, hex_editor->model->string_offset);
|
||||
|
||||
hex_editor->model->mode = 1;
|
||||
}
|
||||
} else {
|
||||
off = 1;
|
||||
if(event.type == InputTypeRepeat) {
|
||||
off = 4;
|
||||
}
|
||||
if(event.key == InputKeyRight) {
|
||||
hex_editor->model->editable_char += off;
|
||||
}
|
||||
if(event.key == InputKeyLeft) {
|
||||
hex_editor->model->editable_char -= off;
|
||||
}
|
||||
|
||||
if(event.key == InputKeyOk) {
|
||||
if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) {
|
||||
FURI_LOG_E(TAG, "Unable to seek stream");
|
||||
break;
|
||||
}
|
||||
stream_seek_to_char(
|
||||
hex_editor->model->stream, '\n', StreamDirectionBackward);
|
||||
stream_seek(
|
||||
hex_editor->model->stream,
|
||||
hex_editor->model->string_offset + 1,
|
||||
StreamOffsetFromCurrent);
|
||||
|
||||
stream_write_char(
|
||||
hex_editor->model->stream, hex_editor->model->editable_char);
|
||||
|
||||
hex_editor->model->editable_char = ' ';
|
||||
|
||||
hex_editor->model->mode = 0;
|
||||
|
||||
stream_seek_to_char(
|
||||
hex_editor->model->stream, '\n', StreamDirectionBackward);
|
||||
|
||||
if(!stream_seek(hex_editor->model->stream, 1, StreamOffsetFromCurrent)) {
|
||||
FURI_LOG_E(TAG, "Unable to seek stream");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) {
|
||||
FURI_LOG_T(TAG, "No keys left in dict");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(event.key == InputKeyBack) {
|
||||
break;
|
||||
}
|
||||
// ?
|
||||
view_port_update(hex_editor->view_port);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
furi_string_free(file_path);
|
||||
hex_editor_free(hex_editor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
applications/external/hex_editor/icons/edit_10px.png
vendored
|
Before Width: | Height: | Size: 150 B |
21
applications/external/lightmeter/LICENSE
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Oleksii Kutuzov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
33
applications/external/lightmeter/application.fam
vendored
@@ -1,33 +0,0 @@
|
||||
App(
|
||||
appid="lightmeter",
|
||||
name="[BH1750] Lightmeter",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="lightmeter_app",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=4 * 1024,
|
||||
fap_version=(1, 2),
|
||||
fap_icon="lightmeter.png",
|
||||
fap_category="GPIO",
|
||||
fap_private_libs=[
|
||||
Lib(
|
||||
name="BH1750",
|
||||
cincludes=["."],
|
||||
sources=[
|
||||
"BH1750.c",
|
||||
],
|
||||
),
|
||||
Lib(
|
||||
name="MAX44009",
|
||||
cincludes=["."],
|
||||
sources=[
|
||||
"MAX44009.c",
|
||||
],
|
||||
),
|
||||
],
|
||||
fap_description="Lightmeter app for photography",
|
||||
fap_author="Oleksii Kutuzov",
|
||||
fap_weburl="https://github.com/oleksiikutuzov/flipperzero-lightmeter",
|
||||
fap_icon_assets="icons",
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "lightmeter_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const lightmeter_on_enter_handlers[])(void*) = {
|
||||
#include "lightmeter_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const lightmeter_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "lightmeter_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const lightmeter_on_exit_handlers[])(void* context) = {
|
||||
#include "lightmeter_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers lightmeter_scene_handlers = {
|
||||
.on_enter_handlers = lightmeter_on_enter_handlers,
|
||||
.on_event_handlers = lightmeter_on_event_handlers,
|
||||
.on_exit_handlers = lightmeter_on_exit_handlers,
|
||||
.scene_num = LightMeterAppSceneNum,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) LightMeterAppScene##id,
|
||||
typedef enum {
|
||||
#include "lightmeter_scene_config.h"
|
||||
LightMeterAppSceneNum,
|
||||
} LightMeterAppScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers lightmeter_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "lightmeter_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "lightmeter_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "lightmeter_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,4 +0,0 @@
|
||||
ADD_SCENE(lightmeter, main, Main)
|
||||
ADD_SCENE(lightmeter, config, Config)
|
||||
ADD_SCENE(lightmeter, help, Help)
|
||||
ADD_SCENE(lightmeter, about, About)
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "../../lightmeter.h"
|
||||
|
||||
void lightmeter_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
UNUSED(app);
|
||||
UNUSED(result);
|
||||
UNUSED(type);
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_scene_about_on_enter(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
furi_string_printf(temp_str, "\e#%s\n", "Information");
|
||||
|
||||
furi_string_cat_printf(temp_str, "Version: %s\n", LM_VERSION_APP);
|
||||
furi_string_cat_printf(temp_str, "Developed by: %s\n", LM_DEVELOPED);
|
||||
furi_string_cat_printf(temp_str, "Github: %s\n\n", LM_GITHUB);
|
||||
|
||||
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"Showing suggested camera\nsettings based on ambient\nlight or flash.\n\nInspired by a lightmeter\nproject by vpominchuk\n");
|
||||
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! \e!\n",
|
||||
false);
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
2,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! Lightmeter \e!\n",
|
||||
false);
|
||||
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewAbout);
|
||||
}
|
||||
|
||||
bool lightmeter_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
bool consumed = false;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void lightmeter_scene_about_on_exit(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
// Clear views
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
#include "../../lightmeter.h"
|
||||
|
||||
#define TAG "Scene Config"
|
||||
|
||||
static const char* iso_numbers[] = {
|
||||
[ISO_6] = "6",
|
||||
[ISO_12] = "12",
|
||||
[ISO_25] = "25",
|
||||
[ISO_50] = "50",
|
||||
[ISO_100] = "100",
|
||||
[ISO_200] = "200",
|
||||
[ISO_400] = "400",
|
||||
[ISO_800] = "800",
|
||||
[ISO_1600] = "1600",
|
||||
[ISO_3200] = "3200",
|
||||
[ISO_6400] = "6400",
|
||||
[ISO_12800] = "12800",
|
||||
[ISO_25600] = "25600",
|
||||
[ISO_51200] = "51200",
|
||||
[ISO_102400] = "102400",
|
||||
};
|
||||
|
||||
static const char* nd_numbers[] = {
|
||||
[ND_0] = "0",
|
||||
[ND_2] = "2",
|
||||
[ND_4] = "4",
|
||||
[ND_8] = "8",
|
||||
[ND_16] = "16",
|
||||
[ND_32] = "32",
|
||||
[ND_64] = "64",
|
||||
[ND_128] = "128",
|
||||
[ND_256] = "256",
|
||||
[ND_512] = "512",
|
||||
[ND_1024] = "1024",
|
||||
[ND_2048] = "2048",
|
||||
[ND_4096] = "4096",
|
||||
};
|
||||
|
||||
static const char* diffusion_dome[] = {
|
||||
[WITHOUT_DOME] = "No",
|
||||
[WITH_DOME] = "Yes",
|
||||
};
|
||||
|
||||
static const char* backlight[] = {
|
||||
[BACKLIGHT_AUTO] = "Auto",
|
||||
[BACKLIGHT_ON] = "On",
|
||||
};
|
||||
|
||||
static const char* lux_only[] = {
|
||||
[LUX_ONLY_OFF] = "Off",
|
||||
[LUX_ONLY_ON] = "On",
|
||||
};
|
||||
|
||||
static const char* sensor_type[] = {
|
||||
[SENSOR_BH1750] = "BH1750",
|
||||
[SENSOR_MAX44009] = "MAX44009",
|
||||
};
|
||||
|
||||
static const char* measurement_resolution[] = {
|
||||
[LOW_RES] = "Low",
|
||||
[HIGH_RES] = "High",
|
||||
[HIGH_RES2] = "High2",
|
||||
};
|
||||
|
||||
static const char* device_addr_bh1750[] = {
|
||||
[ADDR_LOW] = "0x23",
|
||||
[ADDR_HIGH] = "0x5C",
|
||||
};
|
||||
|
||||
static const char* device_addr_max44009[] = {
|
||||
[ADDR_LOW] = "0x4A",
|
||||
[ADDR_HIGH] = "0x4B",
|
||||
};
|
||||
|
||||
enum LightMeterSubmenuIndex {
|
||||
LightMeterSubmenuIndexISO,
|
||||
LightMeterSubmenuIndexND,
|
||||
LightMeterSubmenuIndexDome,
|
||||
LightMeterSubmenuIndexBacklight,
|
||||
LightMeterSubmenuIndexLuxMeter,
|
||||
LightMeterSubmenuIndexSensorType,
|
||||
LightMeterSubmenuIndexMeasurementResolution,
|
||||
LightMeterSubmenuIndexI2CAddress,
|
||||
LightMeterSubmenuIndexHelp,
|
||||
LightMeterSubmenuIndexAbout,
|
||||
};
|
||||
|
||||
static void iso_numbers_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, iso_numbers[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->iso = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void nd_numbers_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, nd_numbers[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->nd = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void dome_presence_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, diffusion_dome[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->dome = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void backlight_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, backlight[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
if(index != config->backlight) {
|
||||
if(index == BACKLIGHT_ON) {
|
||||
notification_message(
|
||||
app->notifications,
|
||||
&sequence_display_backlight_enforce_on); // force on backlight
|
||||
} else {
|
||||
notification_message(
|
||||
app->notifications,
|
||||
&sequence_display_backlight_enforce_auto); // force auto backlight
|
||||
}
|
||||
}
|
||||
config->backlight = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void lux_only_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, lux_only[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->lux_only = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void measurement_resolution_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, measurement_resolution[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->measurement_resolution = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
|
||||
lightmeter_app_i2c_init_sensor(app);
|
||||
}
|
||||
|
||||
static void update_item_addr(LightMeterApp* app) {
|
||||
VariableItem* item = app->var_item_addr;
|
||||
switch(app->config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
variable_item_set_current_value_index(item, app->config->device_addr);
|
||||
variable_item_set_current_value_text(item, device_addr_bh1750[app->config->device_addr]);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
variable_item_set_current_value_index(item, app->config->device_addr);
|
||||
variable_item_set_current_value_text(item, device_addr_max44009[app->config->device_addr]);
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %ld", app->config->sensor_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void device_addr_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
switch(app->config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
variable_item_set_current_value_text(item, device_addr_bh1750[index]);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
variable_item_set_current_value_text(item, device_addr_max44009[index]);
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %ld", app->config->sensor_type);
|
||||
return;
|
||||
}
|
||||
// variable_item_set_current_value_text(item, device_addr[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->device_addr = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
|
||||
lightmeter_app_i2c_init_sensor(app);
|
||||
}
|
||||
|
||||
static void sensor_type_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, sensor_type[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->sensor_type = index;
|
||||
|
||||
update_item_addr(app);
|
||||
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void ok_cb(void* context, uint32_t index) {
|
||||
LightMeterApp* app = context;
|
||||
UNUSED(app);
|
||||
switch(index) {
|
||||
case LightMeterSubmenuIndexHelp:
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventHelp);
|
||||
break;
|
||||
case LightMeterSubmenuIndexAbout:
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventAbout);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_scene_config_on_enter(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
VariableItemList* var_item_list = app->var_item_list;
|
||||
VariableItem* item;
|
||||
LightMeterConfig* config = app->config;
|
||||
|
||||
item =
|
||||
variable_item_list_add(var_item_list, "ISO", COUNT_OF(iso_numbers), iso_numbers_cb, app);
|
||||
variable_item_set_current_value_index(item, config->iso);
|
||||
variable_item_set_current_value_text(item, iso_numbers[config->iso]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "ND factor", COUNT_OF(nd_numbers), nd_numbers_cb, app);
|
||||
variable_item_set_current_value_index(item, config->nd);
|
||||
variable_item_set_current_value_text(item, nd_numbers[config->nd]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "Diffusion dome", COUNT_OF(diffusion_dome), dome_presence_cb, app);
|
||||
variable_item_set_current_value_index(item, config->dome);
|
||||
variable_item_set_current_value_text(item, diffusion_dome[config->dome]);
|
||||
|
||||
item =
|
||||
variable_item_list_add(var_item_list, "Backlight", COUNT_OF(backlight), backlight_cb, app);
|
||||
variable_item_set_current_value_index(item, config->backlight);
|
||||
variable_item_set_current_value_text(item, backlight[config->backlight]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "Lux meter only", COUNT_OF(lux_only), lux_only_cb, app);
|
||||
variable_item_set_current_value_index(item, config->lux_only);
|
||||
variable_item_set_current_value_text(item, lux_only[config->lux_only]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "Sensor", COUNT_OF(sensor_type), sensor_type_cb, app);
|
||||
variable_item_set_current_value_index(item, config->sensor_type);
|
||||
variable_item_set_current_value_text(item, sensor_type[config->sensor_type]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list,
|
||||
"Resolution",
|
||||
COUNT_OF(measurement_resolution),
|
||||
measurement_resolution_cb,
|
||||
app);
|
||||
variable_item_set_current_value_index(item, config->measurement_resolution);
|
||||
variable_item_set_current_value_text(
|
||||
item, measurement_resolution[config->measurement_resolution]);
|
||||
|
||||
switch(config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "I2C address", COUNT_OF(device_addr_bh1750), device_addr_cb, app);
|
||||
variable_item_set_current_value_index(item, config->device_addr);
|
||||
variable_item_set_current_value_text(item, device_addr_bh1750[config->device_addr]);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "I2C address", COUNT_OF(device_addr_max44009), device_addr_cb, app);
|
||||
variable_item_set_current_value_index(item, config->device_addr);
|
||||
variable_item_set_current_value_text(item, device_addr_max44009[config->device_addr]);
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %ld", config->sensor_type);
|
||||
return;
|
||||
}
|
||||
app->var_item_addr = item;
|
||||
update_item_addr(app);
|
||||
|
||||
item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL);
|
||||
item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL);
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
var_item_list,
|
||||
scene_manager_get_scene_state(app->scene_manager, LightMeterAppSceneConfig));
|
||||
|
||||
variable_item_list_set_enter_callback(var_item_list, ok_cb, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewVarItemList);
|
||||
}
|
||||
|
||||
bool lightmeter_scene_config_on_event(void* context, SceneManagerEvent event) {
|
||||
LightMeterApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeTick) {
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case LightMeterAppCustomEventHelp:
|
||||
scene_manager_next_scene(app->scene_manager, LightMeterAppSceneHelp);
|
||||
consumed = true;
|
||||
break;
|
||||
case LightMeterAppCustomEventAbout:
|
||||
scene_manager_next_scene(app->scene_manager, LightMeterAppSceneAbout);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void lightmeter_scene_config_on_exit(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
main_view_set_iso(app->main_view, app->config->iso);
|
||||
main_view_set_nd(app->main_view, app->config->nd);
|
||||
main_view_set_dome(app->main_view, app->config->dome);
|
||||
main_view_set_lux_only(app->main_view, app->config->lux_only);
|
||||
main_view_set_measurement_resolution(app->main_view, app->config->measurement_resolution);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#include "../../lightmeter.h"
|
||||
|
||||
void lightmeter_scene_help_on_enter(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
furi_string_printf(
|
||||
temp_str,
|
||||
"App works with BH1750/MAX44009\nambient light sensor\nconnected via I2C interface\n\n");
|
||||
furi_string_cat(temp_str, "\e#Pinout:\r\n");
|
||||
furi_string_cat(
|
||||
temp_str,
|
||||
" VCC: 3.3V\r\n"
|
||||
" GND: GND\r\n"
|
||||
" SDA: 15 [C1]\r\n"
|
||||
" SCL: 16 [C0]\r\n");
|
||||
furi_string_cat(temp_str, "\r\n\e#Resolutions:\r\n");
|
||||
furi_string_cat(
|
||||
temp_str,
|
||||
"Low: 4.0lx (16ms, 0-54k)\r\n"
|
||||
"High: 1.0lx (120ms, 0-54k)\r\n"
|
||||
"High2: 0.5lx (120ms, 0-27k)\r\n");
|
||||
|
||||
widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewHelp);
|
||||
}
|
||||
|
||||
bool lightmeter_scene_help_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void lightmeter_scene_help_on_exit(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "../../lightmeter.h"
|
||||
|
||||
static void lightmeter_scene_main_on_left(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventConfig);
|
||||
}
|
||||
|
||||
static void lightmeter_scene_main_on_right(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventReset);
|
||||
}
|
||||
|
||||
void lightmeter_scene_main_on_enter(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
main_view_set_iso(app->main_view, app->config->iso);
|
||||
main_view_set_nd(app->main_view, app->config->nd);
|
||||
main_view_set_dome(app->main_view, app->config->dome);
|
||||
main_view_set_lux_only(app->main_view, app->config->lux_only);
|
||||
main_view_set_measurement_resolution(app->main_view, app->config->measurement_resolution);
|
||||
|
||||
lightmeter_main_view_set_left_callback(app->main_view, lightmeter_scene_main_on_left, app);
|
||||
lightmeter_main_view_set_right_callback(app->main_view, lightmeter_scene_main_on_right, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewMainView);
|
||||
}
|
||||
|
||||
bool lightmeter_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
bool response = false;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
if(event.event == LightMeterAppCustomEventConfig) {
|
||||
scene_manager_next_scene(app->scene_manager, LightMeterAppSceneConfig);
|
||||
response = true;
|
||||
} else if(event.event == LightMeterAppCustomEventReset) {
|
||||
lightmeter_app_reset_callback(app);
|
||||
response = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SceneManagerEventTypeTick:
|
||||
lightmeter_app_i2c_callback(app);
|
||||
response = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void lightmeter_scene_main_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,548 +0,0 @@
|
||||
#include "main_view.h"
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include "../../lightmeter.h"
|
||||
#include "../../lightmeter_helper.h"
|
||||
|
||||
#define WORKER_TAG "Main View"
|
||||
|
||||
static const int iso_numbers[] = {
|
||||
[ISO_6] = 6,
|
||||
[ISO_12] = 12,
|
||||
[ISO_25] = 25,
|
||||
[ISO_50] = 50,
|
||||
[ISO_100] = 100,
|
||||
[ISO_200] = 200,
|
||||
[ISO_400] = 400,
|
||||
[ISO_800] = 800,
|
||||
[ISO_1600] = 1600,
|
||||
[ISO_3200] = 3200,
|
||||
[ISO_6400] = 6400,
|
||||
[ISO_12800] = 12800,
|
||||
[ISO_25600] = 25600,
|
||||
[ISO_51200] = 51200,
|
||||
[ISO_102400] = 102400,
|
||||
};
|
||||
|
||||
static const int nd_numbers[] = {
|
||||
[ND_0] = 0,
|
||||
[ND_2] = 2,
|
||||
[ND_4] = 4,
|
||||
[ND_8] = 8,
|
||||
[ND_16] = 16,
|
||||
[ND_32] = 32,
|
||||
[ND_64] = 64,
|
||||
[ND_128] = 128,
|
||||
[ND_256] = 256,
|
||||
[ND_512] = 512,
|
||||
[ND_1024] = 1024,
|
||||
[ND_2048] = 2048,
|
||||
[ND_4096] = 4096,
|
||||
};
|
||||
|
||||
const float aperture_numbers[] = {
|
||||
[AP_1] = 1.0,
|
||||
[AP_1_4] = 1.4,
|
||||
[AP_2] = 2.0,
|
||||
[AP_2_8] = 2.8,
|
||||
[AP_4] = 4.0,
|
||||
[AP_5_6] = 5.6,
|
||||
[AP_8] = 8,
|
||||
[AP_11] = 11,
|
||||
[AP_16] = 16,
|
||||
[AP_22] = 22,
|
||||
[AP_32] = 32,
|
||||
[AP_45] = 45,
|
||||
[AP_64] = 64,
|
||||
[AP_90] = 90,
|
||||
[AP_128] = 128,
|
||||
};
|
||||
|
||||
const float speed_numbers[] = {
|
||||
[SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000,
|
||||
[SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250,
|
||||
[SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_48] = 1.0 / 48,
|
||||
[SPEED_30] = 1.0 / 30, [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8,
|
||||
[SPEED_4] = 1.0 / 4, [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0,
|
||||
[SPEED_2S] = 2.0, [SPEED_4S] = 4.0, [SPEED_8S] = 8.0,
|
||||
[SPEED_15S] = 15.0, [SPEED_30S] = 30.0,
|
||||
};
|
||||
|
||||
struct MainView {
|
||||
View* view;
|
||||
LightMeterMainViewButtonCallback cb_left;
|
||||
LightMeterMainViewButtonCallback cb_right;
|
||||
void* cb_context;
|
||||
};
|
||||
|
||||
void lightmeter_main_view_set_left_callback(
|
||||
MainView* lightmeter_main_view,
|
||||
LightMeterMainViewButtonCallback callback,
|
||||
void* context) {
|
||||
with_view_model(
|
||||
lightmeter_main_view->view,
|
||||
MainViewModel * model,
|
||||
{
|
||||
UNUSED(model);
|
||||
lightmeter_main_view->cb_left = callback;
|
||||
lightmeter_main_view->cb_context = context;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void lightmeter_main_view_set_right_callback(
|
||||
MainView* lightmeter_main_view,
|
||||
LightMeterMainViewButtonCallback callback,
|
||||
void* context) {
|
||||
with_view_model(
|
||||
lightmeter_main_view->view,
|
||||
MainViewModel * model,
|
||||
{
|
||||
UNUSED(model);
|
||||
lightmeter_main_view->cb_right = callback;
|
||||
lightmeter_main_view->cb_context = context;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void main_view_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
MainViewModel* model = context;
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
// draw button
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_button_left(canvas, "Config");
|
||||
|
||||
if(!model->lux_only) {
|
||||
// top row
|
||||
draw_top_row(canvas, model);
|
||||
|
||||
// add f, T values
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
|
||||
// draw f icon and number
|
||||
canvas_draw_icon(canvas, 15, 17, &I_f_10x14);
|
||||
draw_aperture(canvas, model);
|
||||
|
||||
// draw T icon and number
|
||||
canvas_draw_icon(canvas, 15, 34, &I_T_10x14);
|
||||
draw_speed(canvas, model);
|
||||
|
||||
// draw ND number
|
||||
draw_nd_number(canvas, model);
|
||||
|
||||
// draw EV number
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
draw_EV_number(canvas, model);
|
||||
|
||||
// draw mode indicator
|
||||
draw_mode_indicator(canvas, model);
|
||||
} else {
|
||||
elements_button_right(canvas, "Reset");
|
||||
draw_lux_only_mode(canvas, model);
|
||||
}
|
||||
}
|
||||
|
||||
static void main_view_process(MainView* main_view, InputEvent* event) {
|
||||
with_view_model(
|
||||
main_view->view,
|
||||
MainViewModel * model,
|
||||
{
|
||||
if(event->type == InputTypePress) {
|
||||
if(event->key == InputKeyUp) {
|
||||
switch(model->current_mode) {
|
||||
case FIXED_APERTURE:
|
||||
if(model->aperture < AP_NUM - 1) model->aperture++;
|
||||
break;
|
||||
|
||||
case FIXED_SPEED:
|
||||
if(model->speed < SPEED_NUM - 1) model->speed++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
switch(model->current_mode) {
|
||||
case FIXED_APERTURE:
|
||||
if(model->aperture > 0) model->aperture--;
|
||||
break;
|
||||
|
||||
case FIXED_SPEED:
|
||||
if(model->speed > 0) model->speed--;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event->key == InputKeyOk) {
|
||||
switch(model->current_mode) {
|
||||
case FIXED_SPEED:
|
||||
model->current_mode = FIXED_APERTURE;
|
||||
break;
|
||||
|
||||
case FIXED_APERTURE:
|
||||
model->current_mode = FIXED_SPEED;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static bool main_view_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
MainView* main_view = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort && event->key == InputKeyLeft) {
|
||||
if(main_view->cb_left) {
|
||||
main_view->cb_left(main_view->cb_context);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event->type == InputTypeShort && event->key == InputKeyRight) {
|
||||
if(main_view->cb_right) {
|
||||
main_view->cb_right(main_view->cb_context);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event->type == InputTypeShort && event->key == InputKeyBack) {
|
||||
} else {
|
||||
main_view_process(main_view, event);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
MainView* main_view_alloc() {
|
||||
MainView* main_view = malloc(sizeof(MainView));
|
||||
main_view->view = view_alloc();
|
||||
view_set_context(main_view->view, main_view);
|
||||
view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(MainViewModel));
|
||||
view_set_draw_callback(main_view->view, main_view_draw_callback);
|
||||
view_set_input_callback(main_view->view, main_view_input_callback);
|
||||
|
||||
return main_view;
|
||||
}
|
||||
|
||||
void main_view_free(MainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
view_free(main_view->view);
|
||||
free(main_view);
|
||||
}
|
||||
|
||||
View* main_view_get_view(MainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
return main_view->view;
|
||||
}
|
||||
|
||||
void main_view_set_lux(MainView* main_view, float val) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view,
|
||||
MainViewModel * model,
|
||||
{
|
||||
model->lux = val;
|
||||
model->peakLux = fmax(model->peakLux, val);
|
||||
|
||||
model->luxHistogram[model->luxHistogramIndex++] = val;
|
||||
model->luxHistogramIndex %= LUX_HISTORGRAM_LENGTH;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void main_view_reset_lux(MainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->peakLux = 0; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_EV(MainView* main_view, float val) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->EV = val; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_response(MainView* main_view, bool val) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->response = val; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_iso(MainView* main_view, int iso) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->iso = iso; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_nd(MainView* main_view, int nd) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->nd = nd; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_aperture(MainView* main_view, int aperture) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->aperture = aperture; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_speed(MainView* main_view, int speed) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->speed = speed; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_dome(MainView* main_view, bool dome) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->dome = dome; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_lux_only(MainView* main_view, bool lux_only) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->lux_only = lux_only; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_measurement_resolution(MainView* main_view, int measurement_resolution) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view,
|
||||
MainViewModel * model,
|
||||
{ model->measurement_resolution = measurement_resolution; },
|
||||
true);
|
||||
}
|
||||
|
||||
void main_view_set_device_addr(MainView* main_view, int device_addr) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->device_addr = device_addr; }, true);
|
||||
}
|
||||
|
||||
void main_view_set_sensor_type(MainView* main_view, int sensor_type) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { model->sensor_type = sensor_type; }, true);
|
||||
}
|
||||
|
||||
bool main_view_get_dome(MainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
bool val = false;
|
||||
with_view_model(
|
||||
main_view->view, MainViewModel * model, { val = model->dome; }, true);
|
||||
return val;
|
||||
}
|
||||
|
||||
void draw_top_row(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
char str[12];
|
||||
|
||||
if(!model->response) {
|
||||
canvas_draw_box(canvas, 0, 0, 128, 12);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 24, 10, "No sensor found");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
model->iso_val = iso_numbers[model->iso];
|
||||
if(model->nd > 0) model->iso_val /= nd_numbers[model->nd];
|
||||
|
||||
if(model->lux > 0) {
|
||||
if(model->current_mode == FIXED_APERTURE) {
|
||||
model->speed_val = 100 * pow(aperture_numbers[model->aperture], 2) /
|
||||
(double)model->iso_val / pow(2, model->EV);
|
||||
} else {
|
||||
model->aperture_val = sqrt(
|
||||
pow(2, model->EV) * (double)model->iso_val *
|
||||
(double)speed_numbers[model->speed] / 100);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO when T:30, f/0 instead of f/128
|
||||
|
||||
canvas_draw_line(canvas, 0, 10, 128, 10);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
// metering mode A – ambient, F – flash
|
||||
// canvas_draw_str_aligned(canvas, 1, 1, AlignLeft, AlignTop, "A");
|
||||
|
||||
snprintf(str, sizeof(str), "ISO: %d", iso_numbers[model->iso]);
|
||||
canvas_draw_str_aligned(canvas, 19, 1, AlignLeft, AlignTop, str);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(str, sizeof(str), "lx: %.0f", (double)model->lux);
|
||||
canvas_draw_str_aligned(canvas, 87, 2, AlignLeft, AlignTop, str);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_aperture(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
char str[12];
|
||||
|
||||
switch(model->current_mode) {
|
||||
case FIXED_APERTURE:
|
||||
if(model->response) {
|
||||
if(model->aperture < AP_8) {
|
||||
snprintf(str, sizeof(str), "/%.1f", (double)aperture_numbers[model->aperture]);
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "/%.0f", (double)aperture_numbers[model->aperture]);
|
||||
}
|
||||
} else {
|
||||
snprintf(str, sizeof(str), " ---");
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 27, 15, AlignLeft, AlignTop, str);
|
||||
break;
|
||||
case FIXED_SPEED:
|
||||
if(model->aperture_val < aperture_numbers[0] || !model->response) {
|
||||
snprintf(str, sizeof(str), " ---");
|
||||
} else if(model->aperture_val < aperture_numbers[AP_8]) {
|
||||
snprintf(str, sizeof(str), "/%.1f", (double)normalizeAperture(model->aperture_val));
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "/%.0f", (double)normalizeAperture(model->aperture_val));
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 27, 15, AlignLeft, AlignTop, str);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_speed(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
char str[12];
|
||||
|
||||
switch(model->current_mode) {
|
||||
case FIXED_APERTURE:
|
||||
if(model->lux > 0 && model->response) {
|
||||
if(model->speed_val < 1 && model->speed_val > 0) {
|
||||
snprintf(str, sizeof(str), ":1/%.0f", 1 / (double)normalizeTime(model->speed_val));
|
||||
} else {
|
||||
snprintf(str, sizeof(str), ":%.0f", (double)normalizeTime(model->speed_val));
|
||||
}
|
||||
} else {
|
||||
snprintf(str, sizeof(str), " ---");
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 27, 34, AlignLeft, AlignTop, str);
|
||||
break;
|
||||
|
||||
case FIXED_SPEED:
|
||||
if(model->response) {
|
||||
if(model->speed < SPEED_1S) {
|
||||
snprintf(str, sizeof(str), ":1/%.0f", 1 / (double)speed_numbers[model->speed]);
|
||||
} else {
|
||||
snprintf(str, sizeof(str), ":%.0f", (double)speed_numbers[model->speed]);
|
||||
}
|
||||
} else {
|
||||
snprintf(str, sizeof(str), " ---");
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 27, 34, AlignLeft, AlignTop, str);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_mode_indicator(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
switch(model->current_mode) {
|
||||
case FIXED_SPEED:
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 3, 36, AlignLeft, AlignTop, "*");
|
||||
break;
|
||||
|
||||
case FIXED_APERTURE:
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignTop, "*");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_nd_number(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
char str[9];
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(model->response) {
|
||||
snprintf(str, sizeof(str), "ND: %d", nd_numbers[model->nd]);
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "ND: ---");
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 87, 20, AlignLeft, AlignBottom, str);
|
||||
}
|
||||
|
||||
void draw_EV_number(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
char str[7];
|
||||
|
||||
if(model->lux > 0 && model->response) {
|
||||
snprintf(str, sizeof(str), "EV: %1.0f", (double)model->EV);
|
||||
canvas_draw_str_aligned(canvas, 87, 29, AlignLeft, AlignBottom, str);
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 87, 29, AlignLeft, AlignBottom, "EV: --");
|
||||
}
|
||||
}
|
||||
|
||||
void draw_lux_only_mode(Canvas* canvas, MainViewModel* context) {
|
||||
MainViewModel* model = context;
|
||||
|
||||
if(!model->response) {
|
||||
canvas_draw_box(canvas, 0, 0, 128, 12);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 24, 10, "No sensor found");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
char str[12];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
canvas_draw_line(canvas, 0, 10, 128, 10);
|
||||
canvas_draw_str_aligned(canvas, 64, 1, AlignCenter, AlignTop, "Lux meter mode");
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
snprintf(str, sizeof(str), "%.0f", (double)model->lux);
|
||||
canvas_draw_str_aligned(canvas, 80, 22, AlignRight, AlignCenter, str);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 85, 29, AlignLeft, AlignBottom, "Lux now");
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
snprintf(str, sizeof(str), "%.0f", (double)model->peakLux);
|
||||
canvas_draw_str_aligned(canvas, 80, 39, AlignRight, AlignCenter, str);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 85, 43, AlignLeft, AlignBottom, "Lux peak");
|
||||
|
||||
for(int i = 0; i < LUX_HISTORGRAM_LENGTH; i++) {
|
||||
float lux =
|
||||
model->luxHistogram[(i + model->luxHistogramIndex) % LUX_HISTORGRAM_LENGTH];
|
||||
int barHeight = log10(lux) / log10(LUX_HISTORGRAM_LOGBASE);
|
||||
canvas_draw_line(
|
||||
canvas,
|
||||
LUX_HISTORGRAM_LEFT + i,
|
||||
LUX_HISTORGRAM_BOTTOM,
|
||||
LUX_HISTORGRAM_LEFT + i,
|
||||
LUX_HISTORGRAM_BOTTOM - barHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "lightmeter_icons.h"
|
||||
#include "../../lightmeter_config.h"
|
||||
|
||||
/* log base 1.4 and 12 pixels cut off
|
||||
makes it show values approx 65-65k
|
||||
with reasonable resolution in 1-10k range
|
||||
on 20px of screen height */
|
||||
#define LUX_HISTORGRAM_LOGBASE 1.4
|
||||
#define LUX_HISTORGRAM_BOTTOM 64 + 12
|
||||
|
||||
/* 40 pixels between 45th and 85th
|
||||
between left and right button labels */
|
||||
#define LUX_HISTORGRAM_LEFT 45
|
||||
#define LUX_HISTORGRAM_LENGTH 40
|
||||
|
||||
typedef struct MainView MainView;
|
||||
|
||||
typedef enum {
|
||||
FIXED_APERTURE,
|
||||
FIXED_SPEED,
|
||||
|
||||
MODES_SIZE
|
||||
} MainViewMode;
|
||||
|
||||
typedef struct {
|
||||
uint8_t recv[2];
|
||||
MainViewMode current_mode;
|
||||
float lux;
|
||||
float peakLux;
|
||||
float EV;
|
||||
float aperture_val;
|
||||
float speed_val;
|
||||
int iso_val;
|
||||
bool response;
|
||||
int iso;
|
||||
int nd;
|
||||
int aperture;
|
||||
int speed;
|
||||
bool dome;
|
||||
bool lux_only;
|
||||
int measurement_resolution;
|
||||
int device_addr;
|
||||
int sensor_type;
|
||||
|
||||
float luxHistogram[LUX_HISTORGRAM_LENGTH];
|
||||
int luxHistogramIndex;
|
||||
} MainViewModel;
|
||||
|
||||
typedef void (*LightMeterMainViewButtonCallback)(void* context);
|
||||
|
||||
void lightmeter_main_view_set_left_callback(
|
||||
MainView* lightmeter_main_view,
|
||||
LightMeterMainViewButtonCallback callback,
|
||||
void* context);
|
||||
|
||||
void lightmeter_main_view_set_right_callback(
|
||||
MainView* lightmeter_main_view,
|
||||
LightMeterMainViewButtonCallback callback,
|
||||
void* context);
|
||||
|
||||
MainView* main_view_alloc();
|
||||
|
||||
void main_view_free(MainView* main_view);
|
||||
|
||||
View* main_view_get_view(MainView* main_view);
|
||||
|
||||
void main_view_set_lux(MainView* main_view, float val);
|
||||
|
||||
void main_view_reset_lux(MainView* main_view);
|
||||
|
||||
void main_view_set_EV(MainView* main_view_, float val);
|
||||
|
||||
void main_view_set_response(MainView* main_view_, bool val);
|
||||
|
||||
void main_view_set_iso(MainView* main_view, int val);
|
||||
|
||||
void main_view_set_nd(MainView* main_view, int val);
|
||||
|
||||
void main_view_set_aperture(MainView* main_view, int val);
|
||||
|
||||
void main_view_set_speed(MainView* main_view, int val);
|
||||
|
||||
void main_view_set_dome(MainView* main_view, bool val);
|
||||
|
||||
void main_view_set_lux_only(MainView* main_view, bool val);
|
||||
|
||||
void main_view_set_measurement_resolution(MainView* main_view, int val);
|
||||
|
||||
void main_view_set_device_addr(MainView* main_view, int addr);
|
||||
|
||||
void main_view_set_sensor_type(MainView* main_view, int sensor_type);
|
||||
|
||||
bool main_view_get_dome(MainView* main_view);
|
||||
|
||||
void draw_top_row(Canvas* canvas, MainViewModel* context);
|
||||
|
||||
void draw_aperture(Canvas* canvas, MainViewModel* context);
|
||||
|
||||
void draw_speed(Canvas* canvas, MainViewModel* context);
|
||||
|
||||
void draw_mode_indicator(Canvas* canvas, MainViewModel* context);
|
||||
|
||||
void draw_nd_number(Canvas* canvas, MainViewModel* context);
|
||||
|
||||
void draw_EV_number(Canvas* canvas, MainViewModel* context);
|
||||
|
||||
void draw_lux_only_mode(Canvas* canvas, MainViewModel* context);
|
||||
BIN
applications/external/lightmeter/icons/T_10x14.png
vendored
|
Before Width: | Height: | Size: 194 B |
BIN
applications/external/lightmeter/icons/f_10x14.png
vendored
|
Before Width: | Height: | Size: 224 B |
150
applications/external/lightmeter/lib/BH1750/BH1750.c
vendored
@@ -1,150 +0,0 @@
|
||||
/**
|
||||
* @file BH1750.h
|
||||
* @author Oleksii Kutuzov (oleksii.kutuzov@icloud.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2022-11-06
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
* Ported from:
|
||||
* https://github.com/lamik/Light_Sensors_STM32
|
||||
*/
|
||||
|
||||
#include "BH1750.h"
|
||||
|
||||
BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode
|
||||
uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value
|
||||
uint8_t bh1750_addr = BH1750_ADDRESS;
|
||||
|
||||
BH1750_STATUS bh1750_init() {
|
||||
if(BH1750_OK == bh1750_reset()) {
|
||||
if(BH1750_OK == bh1750_set_mt_reg(BH1750_DEFAULT_MTREG)) {
|
||||
return BH1750_OK;
|
||||
}
|
||||
}
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_init_with_addr(uint8_t addr) {
|
||||
bh1750_addr = (addr << 1);
|
||||
return bh1750_init();
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_reset() {
|
||||
uint8_t command = 0x07;
|
||||
bool status;
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &command, 1, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
|
||||
if(status) {
|
||||
return BH1750_OK;
|
||||
}
|
||||
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn) {
|
||||
PowerOn = (PowerOn ? 1 : 0);
|
||||
bool status;
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &PowerOn, 1, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
|
||||
if(status) {
|
||||
return BH1750_OK;
|
||||
}
|
||||
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_set_mode(BH1750_mode mode) {
|
||||
if(!((mode >> 4) || (mode >> 5))) {
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
if((mode & 0x0F) > 3) {
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
bool status;
|
||||
|
||||
bh1750_mode = mode;
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &mode, 1, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
|
||||
if(status) {
|
||||
return BH1750_OK;
|
||||
}
|
||||
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_set_mt_reg(uint8_t mt_reg) {
|
||||
if(mt_reg < 31 || mt_reg > 254) {
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
bh1750_mt_reg = mt_reg;
|
||||
|
||||
uint8_t tmp[2];
|
||||
bool status;
|
||||
|
||||
tmp[0] = (0x40 | (mt_reg >> 5));
|
||||
tmp[1] = (0x60 | (mt_reg & 0x1F));
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[0], 1, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
if(!status) {
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[1], 1, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
if(status) {
|
||||
return BH1750_OK;
|
||||
}
|
||||
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_trigger_manual_conversion() {
|
||||
if(BH1750_OK == bh1750_set_mode(bh1750_mode)) {
|
||||
return BH1750_OK;
|
||||
}
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
|
||||
BH1750_STATUS bh1750_read_light(float* result) {
|
||||
float result_tmp;
|
||||
uint8_t rcv[2];
|
||||
bool status;
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
status = furi_hal_i2c_rx(I2C_BUS, bh1750_addr, rcv, 2, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
|
||||
if(status) {
|
||||
result_tmp = (rcv[0] << 8) | (rcv[1]);
|
||||
|
||||
if(bh1750_mt_reg != BH1750_DEFAULT_MTREG) {
|
||||
result_tmp *= (float)((uint8_t)BH1750_DEFAULT_MTREG / (float)bh1750_mt_reg);
|
||||
}
|
||||
|
||||
if(bh1750_mode == ONETIME_HIGH_RES_MODE_2 || bh1750_mode == CONTINUOUS_HIGH_RES_MODE_2) {
|
||||
result_tmp /= 2.0;
|
||||
}
|
||||
|
||||
*result = result_tmp / BH1750_CONVERSION_FACTOR;
|
||||
|
||||
return BH1750_OK;
|
||||
}
|
||||
return BH1750_ERROR;
|
||||
}
|
||||
110
applications/external/lightmeter/lib/BH1750/BH1750.h
vendored
@@ -1,110 +0,0 @@
|
||||
/**
|
||||
* @file BH1750.h
|
||||
* @author Oleksii Kutuzov (oleksii.kutuzov@icloud.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2022-11-06
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
* Ported from:
|
||||
* https://github.com/lamik/Light_Sensors_STM32
|
||||
*/
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#ifndef BH1750_H_
|
||||
#define BH1750_H_
|
||||
|
||||
// I2C BUS
|
||||
#define I2C_BUS &furi_hal_i2c_handle_external
|
||||
#define I2C_TIMEOUT 10
|
||||
|
||||
#define BH1750_ADDRESS (0x23 << 1)
|
||||
|
||||
#define BH1750_POWER_DOWN 0x00
|
||||
#define BH1750_POWER_ON 0x01
|
||||
#define BH1750_RESET 0x07
|
||||
#define BH1750_DEFAULT_MTREG 69
|
||||
#define BH1750_DEFAULT_MODE ONETIME_HIGH_RES_MODE
|
||||
|
||||
#define BH1750_CONVERSION_FACTOR 1.2
|
||||
|
||||
typedef enum { BH1750_OK = 0, BH1750_ERROR = 1 } BH1750_STATUS;
|
||||
|
||||
typedef enum {
|
||||
CONTINUOUS_HIGH_RES_MODE = 0x10,
|
||||
CONTINUOUS_HIGH_RES_MODE_2 = 0x11,
|
||||
CONTINUOUS_LOW_RES_MODE = 0x13,
|
||||
ONETIME_HIGH_RES_MODE = 0x20,
|
||||
ONETIME_HIGH_RES_MODE_2 = 0x21,
|
||||
ONETIME_LOW_RES_MODE = 0x23
|
||||
} BH1750_mode;
|
||||
|
||||
/**
|
||||
* @brief Initialize the sensor. Sends the reset command and sets the measurement register to the default value.
|
||||
*
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_init();
|
||||
|
||||
/**
|
||||
* @brief Change the I2C device address and then initialize the sensor.
|
||||
*
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_init_with_addr(uint8_t addr);
|
||||
|
||||
/**
|
||||
* @brief Reset all registers to the default value.
|
||||
*
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_reset();
|
||||
|
||||
/**
|
||||
* @brief Sets the power state. 1 - running; 0 - sleep, low power.
|
||||
*
|
||||
* @param PowerOn sensor state.
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn);
|
||||
|
||||
/**
|
||||
* @brief Set the Measurement Time register. It allows to increase or decrease the sensitivity.
|
||||
*
|
||||
* @param MTreg value from 31 to 254, defaults to 69.
|
||||
*
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_set_mt_reg(uint8_t MTreg);
|
||||
|
||||
/**
|
||||
* @brief Set the mode of converting. Look into the bh1750_mode enum.
|
||||
*
|
||||
* @param Mode mode enumerator
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_set_mode(BH1750_mode Mode);
|
||||
|
||||
/**
|
||||
* @brief Trigger the conversion in manual modes.
|
||||
*
|
||||
* @details a low-resolution mode, the conversion time is typically 16 ms, and for a high-resolution
|
||||
* mode is 120 ms. You need to wait until reading the measurement value. There is no need
|
||||
* to exit low-power mode for manual conversion. It makes automatically.
|
||||
*
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_trigger_manual_conversion();
|
||||
|
||||
/**
|
||||
* @brief Read the converted value and calculate the result.
|
||||
*
|
||||
* @param Result stores received value to this variable.
|
||||
* @return BH1750_STATUS
|
||||
*/
|
||||
BH1750_STATUS bh1750_read_light(float* Result);
|
||||
|
||||
#endif /* BH1750_H_ */
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Oleksii Kutuzov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,35 +0,0 @@
|
||||
#include <MAX44009.h>
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
|
||||
uint8_t max44009_addr = MAX44009_ADDR;
|
||||
|
||||
void max44009_init() {
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
furi_hal_i2c_write_reg_8(
|
||||
I2C_BUS, max44009_addr, MAX44009_REG_CONFIG, MAX44009_REG_CONFIG_CONT_MODE, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
}
|
||||
|
||||
void max44009_init_with_addr(uint8_t addr) {
|
||||
max44009_addr = (addr << 1);
|
||||
return max44009_init();
|
||||
}
|
||||
|
||||
int max44009_read_light(float* result) {
|
||||
uint8_t data_one = 0;
|
||||
uint8_t exp, mantissa;
|
||||
int status;
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
furi_hal_i2c_read_reg_8(I2C_BUS, MAX44009_ADDR, MAX44009_REG_LUX_HI, &data_one, I2C_TIMEOUT);
|
||||
exp = (data_one & MAX44009_REG_LUX_HI_EXP_MASK) >> 4;
|
||||
mantissa = (data_one & MAX44009_REG_LUX_HI_MANT_HI_MASK) << 4;
|
||||
status = furi_hal_i2c_read_reg_8(
|
||||
I2C_BUS, MAX44009_ADDR, MAX44009_REG_LUX_LO, &data_one, I2C_TIMEOUT);
|
||||
mantissa |= (data_one & MAX44009_REG_LUX_LO_MANT_LO_MASK);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
*result = (float)pow(2, exp) * mantissa * 0.045;
|
||||
FURI_LOG_D("MAX44009", "exp %d, mant %d, lux %f", exp, mantissa, (double)*result);
|
||||
return status;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
// I2C BUS
|
||||
#define I2C_BUS &furi_hal_i2c_handle_external
|
||||
#define I2C_TIMEOUT 10
|
||||
|
||||
#define MAX44009_ADDR (0x4A << 1)
|
||||
|
||||
#define MAX44009_REG_INT_STATUS 0x00
|
||||
#define MAX44009_REG_INT_EN 0x01
|
||||
#define MAX44009_REG_CONFIG 0x02
|
||||
#define MAX44009_REG_CONFIG_CONT_MODE (1 << 7)
|
||||
#define MAX44009_REG_LUX_HI 0x03
|
||||
#define MAX44009_REG_LUX_HI_EXP_MASK 0xF0
|
||||
#define MAX44009_REG_LUX_HI_MANT_HI_MASK 0x0F
|
||||
#define MAX44009_REG_LUX_LO 0x04
|
||||
#define MAX44009_REG_LUX_LO_MANT_LO_MASK 0x0F
|
||||
#define MAX44009_REG_THRESH_HI 0x05
|
||||
#define MAX44009_REG_THRESH_LO 0x06
|
||||
#define MAX44009_REG_INT_TIME 0x07
|
||||
|
||||
void max44009_init();
|
||||
void max44009_init_with_addr(uint8_t addr);
|
||||
int max44009_read_light(float* result);
|
||||
259
applications/external/lightmeter/lightmeter.c
vendored
@@ -1,259 +0,0 @@
|
||||
#include "lightmeter.h"
|
||||
#include "lightmeter_helper.h"
|
||||
|
||||
#define TAG "MAIN APP"
|
||||
|
||||
static bool lightmeter_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
LightMeterApp* app = context;
|
||||
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool lightmeter_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
LightMeterApp* app = context;
|
||||
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void lightmeter_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
LightMeterApp* app = context;
|
||||
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
LightMeterApp* lightmeter_app_alloc(uint32_t first_scene) {
|
||||
LightMeterApp* app = malloc(sizeof(LightMeterApp));
|
||||
|
||||
// Set default values to config
|
||||
app->config = malloc(sizeof(LightMeterConfig));
|
||||
app->config->iso = DEFAULT_ISO;
|
||||
app->config->nd = DEFAULT_ND;
|
||||
app->config->aperture = DEFAULT_APERTURE;
|
||||
app->config->dome = DEFAULT_DOME;
|
||||
app->config->backlight = DEFAULT_BACKLIGHT;
|
||||
app->config->measurement_resolution = HIGH_RES;
|
||||
app->config->device_addr = ADDR_LOW;
|
||||
app->config->lux_only = LUX_ONLY_OFF;
|
||||
|
||||
// Records
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->storage = furi_record_open(RECORD_STORAGE);
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
app->cfg_path = furi_string_alloc();
|
||||
furi_string_printf(app->cfg_path, "%s/%s", APP_PATH_DIR, APP_PATH_CFG);
|
||||
|
||||
FlipperFormat* cfg_fmt = flipper_format_file_alloc(app->storage);
|
||||
if(flipper_format_file_open_existing(cfg_fmt, furi_string_get_cstr(app->cfg_path))) {
|
||||
flipper_format_read_int32(cfg_fmt, "iso", &app->config->iso, 1);
|
||||
flipper_format_read_int32(cfg_fmt, "aperture", &app->config->aperture, 1);
|
||||
flipper_format_read_int32(cfg_fmt, "dome", &app->config->dome, 1);
|
||||
flipper_format_read_int32(cfg_fmt, "backlight", &app->config->backlight, 1);
|
||||
flipper_format_read_int32(
|
||||
cfg_fmt, "measurement_resolution", &app->config->measurement_resolution, 1);
|
||||
flipper_format_read_int32(cfg_fmt, "lux_only", &app->config->lux_only, 1);
|
||||
flipper_format_read_int32(cfg_fmt, "device_addr", &app->config->device_addr, 1);
|
||||
flipper_format_read_int32(cfg_fmt, "sensor_type", &app->config->sensor_type, 1);
|
||||
}
|
||||
flipper_format_free(cfg_fmt);
|
||||
|
||||
// Sensor
|
||||
lightmeter_app_i2c_init_sensor(app);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&lightmeter_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, lightmeter_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, lightmeter_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, lightmeter_tick_event_callback, furi_ms_to_ticks(200));
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Views
|
||||
app->main_view = main_view_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, LightMeterAppViewMainView, main_view_get_view(app->main_view));
|
||||
|
||||
// Set default values to main view from config
|
||||
main_view_set_iso(app->main_view, app->config->iso);
|
||||
main_view_set_nd(app->main_view, app->config->nd);
|
||||
main_view_set_aperture(app->main_view, app->config->aperture);
|
||||
main_view_set_speed(app->main_view, DEFAULT_SPEED);
|
||||
main_view_set_dome(app->main_view, app->config->dome);
|
||||
|
||||
// Variable item list
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
LightMeterAppViewVarItemList,
|
||||
variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
// Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, LightMeterAppViewAbout, widget_get_view(app->widget));
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, LightMeterAppViewHelp, widget_get_view(app->widget));
|
||||
|
||||
// Set first scene
|
||||
scene_manager_next_scene(app->scene_manager, first_scene);
|
||||
return app;
|
||||
}
|
||||
|
||||
void lightmeter_app_free(LightMeterApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewMainView);
|
||||
main_view_free(app->main_view);
|
||||
|
||||
// Variable item list
|
||||
view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewVarItemList);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewAbout);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewHelp);
|
||||
widget_free(app->widget);
|
||||
|
||||
// View dispatcher
|
||||
scene_manager_free(app->scene_manager);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Records
|
||||
furi_record_close(RECORD_GUI);
|
||||
if(app->config->backlight != BACKLIGHT_AUTO) {
|
||||
notification_message(
|
||||
app->notifications,
|
||||
&sequence_display_backlight_enforce_auto); // set backlight back to auto
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
bh1750_set_power_state(0);
|
||||
|
||||
free(app->config);
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t lightmeter_app(void* p) {
|
||||
UNUSED(p);
|
||||
uint32_t first_scene = LightMeterAppSceneMain;
|
||||
LightMeterApp* app = lightmeter_app_alloc(first_scene);
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
lightmeter_app_free(app);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lightmeter_app_set_config(LightMeterApp* context, LightMeterConfig* config) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
app->config = config;
|
||||
storage_common_mkdir(app->storage, APP_PATH_DIR);
|
||||
|
||||
FlipperFormat* cfg_fmt = flipper_format_file_alloc(app->storage);
|
||||
if(flipper_format_file_open_always(cfg_fmt, furi_string_get_cstr(app->cfg_path))) {
|
||||
flipper_format_write_header_cstr(cfg_fmt, "lightmeter", 1);
|
||||
|
||||
flipper_format_write_int32(cfg_fmt, "iso", &(app->config->iso), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "nd", &(app->config->nd), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "aperture", &(app->config->aperture), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "dome", &(app->config->dome), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "backlight", &(app->config->backlight), 1);
|
||||
flipper_format_write_int32(
|
||||
cfg_fmt, "measurement_resolution", &(app->config->measurement_resolution), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "lux_only", &(app->config->lux_only), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "device_addr", &(app->config->device_addr), 1);
|
||||
flipper_format_write_int32(cfg_fmt, "sensor_type", &(app->config->sensor_type), 1);
|
||||
}
|
||||
flipper_format_free(cfg_fmt);
|
||||
}
|
||||
|
||||
void lightmeter_app_i2c_init_sensor(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
switch(app->config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
bh1750_set_power_state(1);
|
||||
switch(app->config->device_addr) {
|
||||
case ADDR_HIGH:
|
||||
bh1750_init_with_addr(0x5C);
|
||||
break;
|
||||
case ADDR_LOW:
|
||||
bh1750_init_with_addr(0x23);
|
||||
break;
|
||||
default:
|
||||
bh1750_init_with_addr(0x23);
|
||||
break;
|
||||
}
|
||||
bh1750_set_mode(ONETIME_HIGH_RES_MODE);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
switch(app->config->device_addr) {
|
||||
case ADDR_HIGH:
|
||||
max44009_init_with_addr(0x4B);
|
||||
break;
|
||||
case ADDR_LOW:
|
||||
max44009_init_with_addr(0x4A);
|
||||
break;
|
||||
default:
|
||||
max44009_init_with_addr(0x4A);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %ld", app->config->sensor_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_app_i2c_deinit_sensor(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
switch(app->config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
bh1750_set_power_state(0);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
// nothing
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %ld", app->config->sensor_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_app_i2c_callback(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
float EV = 0;
|
||||
float lux = 0;
|
||||
bool response = 0;
|
||||
|
||||
if(app->config->sensor_type == SENSOR_BH1750) {
|
||||
if(bh1750_trigger_manual_conversion() == BH1750_OK) {
|
||||
bh1750_read_light(&lux);
|
||||
response = 1;
|
||||
}
|
||||
} else if(app->config->sensor_type == SENSOR_MAX44009) {
|
||||
if(max44009_read_light(&lux)) response = 1;
|
||||
}
|
||||
|
||||
if(main_view_get_dome(app->main_view)) lux *= DOME_COEFFICIENT;
|
||||
EV = lux2ev(lux);
|
||||
|
||||
main_view_set_lux(app->main_view, lux);
|
||||
main_view_set_EV(app->main_view, EV);
|
||||
main_view_set_response(app->main_view, response);
|
||||
}
|
||||
|
||||
void lightmeter_app_reset_callback(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
main_view_reset_lux(app->main_view);
|
||||
}
|
||||
79
applications/external/lightmeter/lightmeter.h
vendored
@@ -1,79 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <stream/stream.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include "gui/views/main_view.h"
|
||||
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
|
||||
#include "gui/scenes/config/lightmeter_scene.h"
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include "lightmeter_config.h"
|
||||
#include <BH1750.h>
|
||||
#include <MAX44009.h>
|
||||
|
||||
#define APP_PATH_DIR STORAGE_APP_DATA_PATH_PREFIX
|
||||
#define APP_PATH_CFG "config.txt"
|
||||
|
||||
typedef struct {
|
||||
int32_t iso;
|
||||
int32_t nd;
|
||||
int32_t aperture;
|
||||
int32_t dome;
|
||||
int32_t backlight;
|
||||
int32_t lux_only;
|
||||
int32_t sensor_type;
|
||||
int32_t measurement_resolution;
|
||||
int32_t device_addr;
|
||||
} LightMeterConfig;
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
MainView* main_view;
|
||||
VariableItemList* var_item_list;
|
||||
VariableItem* var_item_addr;
|
||||
LightMeterConfig* config;
|
||||
NotificationApp* notifications;
|
||||
Widget* widget;
|
||||
|
||||
Storage* storage;
|
||||
FuriString* cfg_path;
|
||||
} LightMeterApp;
|
||||
|
||||
typedef enum {
|
||||
LightMeterAppViewMainView,
|
||||
LightMeterAppViewConfigView,
|
||||
LightMeterAppViewVarItemList,
|
||||
LightMeterAppViewAbout,
|
||||
LightMeterAppViewHelp,
|
||||
} LightMeterAppView;
|
||||
|
||||
typedef enum {
|
||||
LightMeterAppCustomEventReset,
|
||||
LightMeterAppCustomEventConfig,
|
||||
LightMeterAppCustomEventHelp,
|
||||
LightMeterAppCustomEventAbout,
|
||||
} LightMeterAppCustomEvent;
|
||||
|
||||
void lightmeter_app_set_config(LightMeterApp* context, LightMeterConfig* config);
|
||||
|
||||
void lightmeter_app_i2c_init_sensor(LightMeterApp* context);
|
||||
|
||||
void lightmeter_app_i2c_deinit_sensor(LightMeterApp* context);
|
||||
|
||||
void lightmeter_app_i2c_callback(LightMeterApp* context);
|
||||
|
||||
void lightmeter_app_reset_callback(LightMeterApp* context);
|
||||
BIN
applications/external/lightmeter/lightmeter.png
vendored
|
Before Width: | Height: | Size: 243 B |
124
applications/external/lightmeter/lightmeter_config.h
vendored
@@ -1,124 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define LM_VERSION_APP "1.2"
|
||||
#define LM_DEVELOPED "Oleksii Kutuzov"
|
||||
#define LM_GITHUB "https://github.com/oleksiikutuzov/flipperzero-lightmeter"
|
||||
|
||||
#define DOME_COEFFICIENT 2.3
|
||||
#define DEFAULT_ISO ISO_100
|
||||
#define DEFAULT_ND ND_0
|
||||
#define DEFAULT_APERTURE AP_2_8
|
||||
#define DEFAULT_SPEED SPEED_125
|
||||
#define DEFAULT_DOME WITHOUT_DOME
|
||||
#define DEFAULT_BACKLIGHT BACKLIGHT_AUTO
|
||||
|
||||
typedef enum {
|
||||
ISO_6,
|
||||
ISO_12,
|
||||
ISO_25,
|
||||
ISO_50,
|
||||
ISO_100,
|
||||
ISO_200,
|
||||
ISO_400,
|
||||
ISO_800,
|
||||
ISO_1600,
|
||||
ISO_3200,
|
||||
ISO_6400,
|
||||
ISO_12800,
|
||||
ISO_25600,
|
||||
ISO_51200,
|
||||
ISO_102400,
|
||||
|
||||
ISO_NUM,
|
||||
} LightMeterISONumbers;
|
||||
|
||||
typedef enum {
|
||||
ND_0,
|
||||
ND_2,
|
||||
ND_4,
|
||||
ND_8,
|
||||
ND_16,
|
||||
ND_32,
|
||||
ND_64,
|
||||
ND_128,
|
||||
ND_256,
|
||||
ND_512,
|
||||
ND_1024,
|
||||
ND_2048,
|
||||
ND_4096,
|
||||
|
||||
ND_NUM,
|
||||
} LightMeterNDNumbers;
|
||||
|
||||
typedef enum {
|
||||
AP_1,
|
||||
AP_1_4,
|
||||
AP_2,
|
||||
AP_2_8,
|
||||
AP_4,
|
||||
AP_5_6,
|
||||
AP_8,
|
||||
AP_11,
|
||||
AP_16,
|
||||
AP_22,
|
||||
AP_32,
|
||||
AP_45,
|
||||
AP_64,
|
||||
AP_90,
|
||||
AP_128,
|
||||
|
||||
AP_NUM,
|
||||
} LightMeterApertureNumbers;
|
||||
|
||||
typedef enum {
|
||||
SPEED_8000,
|
||||
SPEED_4000,
|
||||
SPEED_2000,
|
||||
SPEED_1000,
|
||||
SPEED_500,
|
||||
SPEED_250,
|
||||
SPEED_125,
|
||||
SPEED_60,
|
||||
SPEED_48,
|
||||
SPEED_30,
|
||||
SPEED_15,
|
||||
SPEED_8,
|
||||
SPEED_4,
|
||||
SPEED_2,
|
||||
SPEED_1S,
|
||||
SPEED_2S,
|
||||
SPEED_4S,
|
||||
SPEED_8S,
|
||||
SPEED_15S,
|
||||
SPEED_30S,
|
||||
|
||||
SPEED_NUM,
|
||||
} LightMeterSpeedNumbers;
|
||||
|
||||
typedef enum {
|
||||
WITHOUT_DOME,
|
||||
WITH_DOME,
|
||||
} LightMeterDomePresence;
|
||||
|
||||
typedef enum {
|
||||
LUX_ONLY_OFF,
|
||||
LUX_ONLY_ON,
|
||||
} LightMeterLuxOnlyMode;
|
||||
|
||||
typedef enum {
|
||||
LOW_RES,
|
||||
HIGH_RES,
|
||||
HIGH_RES2,
|
||||
} LightMeterMeterMode;
|
||||
|
||||
typedef enum {
|
||||
ADDR_LOW,
|
||||
ADDR_HIGH,
|
||||
} LightMeterMeterAddr;
|
||||
|
||||
typedef enum {
|
||||
SENSOR_BH1750,
|
||||
SENSOR_MAX44009,
|
||||
} LightMeterSensorType;
|
||||
|
||||
typedef enum { BACKLIGHT_AUTO, BACKLIGHT_ON } LightMeterBacklight;
|
||||
@@ -1,43 +0,0 @@
|
||||
#include "lightmeter_helper.h"
|
||||
#include "lightmeter_config.h"
|
||||
|
||||
extern const float aperture_numbers[];
|
||||
extern const float speed_numbers[];
|
||||
|
||||
float lux2ev(float lux) {
|
||||
return log2(lux / 2.5);
|
||||
}
|
||||
|
||||
float getMinDistance(float x, float v1, float v2) {
|
||||
if(x - v1 > v2 - x) {
|
||||
return v2;
|
||||
}
|
||||
|
||||
return v1;
|
||||
}
|
||||
|
||||
float normalizeAperture(float a) {
|
||||
for(int i = 0; i < AP_NUM; i++) {
|
||||
float a1 = aperture_numbers[i];
|
||||
float a2 = aperture_numbers[i + 1];
|
||||
|
||||
if(a1 < a && a2 >= a) {
|
||||
return getMinDistance(a, a1, a2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float normalizeTime(float a) {
|
||||
for(int i = 0; i < SPEED_NUM; i++) {
|
||||
float a1 = speed_numbers[i];
|
||||
float a2 = speed_numbers[i + 1];
|
||||
|
||||
if(a1 < a && a2 >= a) {
|
||||
return getMinDistance(a, a1, a2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
|
||||
float lux2ev(float lux);
|
||||
|
||||
float getMinDistance(float x, float v1, float v2);
|
||||
|
||||
float normalizeAperture(float a);
|
||||
|
||||
float normalizeTime(float a);
|
||||
21
applications/external/magspoof/LICENSE
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Zachary Weiss
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
20
applications/external/magspoof/application.fam
vendored
@@ -1,20 +0,0 @@
|
||||
App(
|
||||
appid="magspoof",
|
||||
name="[MAG] MagSpoof",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="mag_app",
|
||||
requires=[
|
||||
"gui",
|
||||
"storage",
|
||||
"notification",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=6 * 1024,
|
||||
fap_icon="icons/mag_10px.png",
|
||||
fap_category="GPIO",
|
||||
fap_icon_assets="icons",
|
||||
fap_version=(0, 5), # major, minor
|
||||
fap_description="WIP MagSpoof port using the RFID subsystem",
|
||||
fap_author="Zachary Weiss",
|
||||
fap_weburl="https://github.com/zacharyweiss/magspoof_flipper",
|
||||
)
|
||||
479
applications/external/magspoof/helpers/mag_helpers.c
vendored
@@ -1,479 +0,0 @@
|
||||
#include "mag_helpers.h"
|
||||
|
||||
#define TAG "MagHelpers"
|
||||
|
||||
// Haviv Board - pins gpio_ext_pa7 & gpio_ext_pa6 was swapped.
|
||||
#define GPIO_PIN_A &gpio_ext_pa7
|
||||
#define GPIO_PIN_B &gpio_ext_pa6
|
||||
#define GPIO_PIN_ENABLE &gpio_ext_pa4
|
||||
#define RFID_PIN_OUT &gpio_rfid_carrier_out
|
||||
|
||||
#define ZERO_PREFIX 25 // n zeros prefix
|
||||
#define ZERO_BETWEEN 53 // n zeros between tracks
|
||||
#define ZERO_SUFFIX 25 // n zeros suffix
|
||||
|
||||
// bits per char on a given track
|
||||
const uint8_t bitlen[] = {7, 5, 5};
|
||||
// char offset by track
|
||||
const int sublen[] = {32, 48, 48};
|
||||
|
||||
uint8_t last_value = 2;
|
||||
|
||||
void play_halfbit(bool value, MagSetting* setting) {
|
||||
switch(setting->tx) {
|
||||
case MagTxStateRFID:
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, value);
|
||||
/*furi_hal_gpio_write(RFID_PIN_OUT, !value);
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, value);
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, !value);
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, value);*/
|
||||
break;
|
||||
case MagTxStateGPIO:
|
||||
furi_hal_gpio_write(GPIO_PIN_A, value);
|
||||
furi_hal_gpio_write(GPIO_PIN_B, !value);
|
||||
break;
|
||||
case MagTxStatePiezo:
|
||||
furi_hal_gpio_write(&gpio_speaker, value);
|
||||
/*furi_hal_gpio_write(&gpio_speaker, !value);
|
||||
furi_hal_gpio_write(&gpio_speaker, value);
|
||||
furi_hal_gpio_write(&gpio_speaker, !value);
|
||||
furi_hal_gpio_write(&gpio_speaker, value);*/
|
||||
|
||||
break;
|
||||
case MagTxStateLF_P:
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, value);
|
||||
furi_hal_gpio_write(&gpio_speaker, value);
|
||||
|
||||
/* // Weaker but cleaner signal
|
||||
if(value) {
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, value);
|
||||
furi_hal_gpio_write(&gpio_speaker, value);
|
||||
furi_delay_us(10);
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, !value);
|
||||
furi_hal_gpio_write(&gpio_speaker, !value);
|
||||
} else {
|
||||
furi_delay_us(10);
|
||||
}*/
|
||||
|
||||
/*furi_hal_gpio_write(RFID_PIN_OUT, value);
|
||||
furi_hal_gpio_write(&gpio_speaker, value);
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, !value);
|
||||
furi_hal_gpio_write(&gpio_speaker, !value);
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, value);
|
||||
furi_hal_gpio_write(&gpio_speaker, value);*/
|
||||
break;
|
||||
case MagTxStateNFC:
|
||||
// turn on for duration of half-bit? or "blip" the field on / off?
|
||||
// getting nothing from the mag reader either way
|
||||
//(value) ? furi_hal_nfc_ll_txrx_on() : furi_hal_nfc_ll_txrx_off();
|
||||
|
||||
if(last_value == 2 || value != (bool)last_value) {
|
||||
furi_hal_nfc_ll_txrx_on();
|
||||
//furi_delay_us(64);
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
}
|
||||
break;
|
||||
case MagTxCC1101_434:
|
||||
case MagTxCC1101_868:
|
||||
if(last_value == 2 || value != (bool)last_value) {
|
||||
furi_hal_gpio_write(&gpio_cc1101_g0, true);
|
||||
furi_delay_us(64);
|
||||
furi_hal_gpio_write(&gpio_cc1101_g0, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
last_value = value;
|
||||
}
|
||||
|
||||
void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse) {
|
||||
for(uint16_t i = 0; i < n_bits; i++) {
|
||||
uint16_t j = (reverse) ? (n_bits - i - 1) : i;
|
||||
uint8_t byte = j / 8;
|
||||
uint8_t bitmask = 1 << (7 - (j % 8));
|
||||
/* Bits are stored in their arrays like on a card (LSB first). This is not how usually bits are stored in a
|
||||
* byte, with the MSB first. the var bitmask creates the pattern to iterate through each bit, LSB first, like so
|
||||
* 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x80... masking bits one by one from the current byte
|
||||
*
|
||||
* I've chosen this LSB approach since bits and bytes are hard enough to visualize with the 5/8 and 7/8 encoding
|
||||
* MSR uses. It's a biiit more complicated to process, but visualizing it with printf or a debugger is
|
||||
* infinitely easier
|
||||
*
|
||||
* Encoding the following pairs of 5 bits as 5/8: A1234 B1234 C1234 D1234
|
||||
* using this LSB format looks like: A1234B12 34C1234D 12340000
|
||||
* using the MSB format, looks like: 21B4321A D4321C43 00004321
|
||||
* this means reading each byte backwards when printing/debugging, and the jumping 16 bits ahead, reading 8 more
|
||||
* bits backward, jumping 16 more bits ahead.
|
||||
*
|
||||
* I find this much more convenient for debugging, with the tiny incovenience of reading the bits in reverse
|
||||
* order. Thus, the reason for the bitmask above
|
||||
*/
|
||||
|
||||
bool bit = !!(bits_manchester[byte] & bitmask);
|
||||
|
||||
// TODO: reimplement timing delays. Replace fixed furi_hal_cortex_delay_us to wait instead to a specific value
|
||||
// for DWT->CYCCNT. Note timer is aliased to 64us as per
|
||||
// #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) | furi_hal_cortex.c
|
||||
|
||||
play_halfbit(bit, setting);
|
||||
furi_delay_us(setting->us_clock);
|
||||
// if (i % 2 == 1) furi_delay_us(setting->us_interpacket);
|
||||
}
|
||||
}
|
||||
|
||||
void tx_init_rfid() {
|
||||
// initialize RFID system for TX
|
||||
|
||||
furi_hal_ibutton_pin_configure();
|
||||
|
||||
// furi_hal_ibutton_start_drive();
|
||||
furi_hal_ibutton_pin_write(false);
|
||||
|
||||
// Initializing at GpioSpeedLow seems sufficient for our needs; no improvements seen by increasing speed setting
|
||||
|
||||
// this doesn't seem to make a difference, leaving it in
|
||||
furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_write(&gpio_rfid_data_in, false);
|
||||
|
||||
// false->ground RFID antenna; true->don't ground
|
||||
// skotopes (RFID dev) say normally you'd want RFID_PULL in high for signal forming, while modulating RFID_OUT
|
||||
// dunaevai135 had it low in their old code. Leaving low, as it doesn't seem to make a difference on my janky antenna
|
||||
furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false);
|
||||
|
||||
furi_hal_gpio_init(RFID_PIN_OUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
furi_delay_ms(300);
|
||||
}
|
||||
|
||||
void tx_deinit_rfid() {
|
||||
// reset RFID system
|
||||
furi_hal_gpio_write(RFID_PIN_OUT, 0);
|
||||
|
||||
furi_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
void tx_init_rf(int hz) {
|
||||
// presets and frequency will need some experimenting
|
||||
furi_hal_subghz_reset();
|
||||
// furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
|
||||
// furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync);
|
||||
// furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync);
|
||||
// furi_hal_subghz_load_preset(FuriHalSubGhzPreset2FSKDev238Async);
|
||||
// furi_hal_subghz_load_preset(FuriHalSubGhzPreset2FSKDev476Async);
|
||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_subghz_set_frequency_and_path(hz);
|
||||
furi_hal_subghz_tx();
|
||||
furi_hal_gpio_write(&gpio_cc1101_g0, false);
|
||||
}
|
||||
|
||||
void tx_init_piezo() {
|
||||
// TODO: some special mutex acquire procedure? c.f. furi_hal_speaker.c
|
||||
furi_hal_gpio_init(&gpio_speaker, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
|
||||
void tx_deinit_piezo() {
|
||||
// TODO: some special mutex release procedure?
|
||||
furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
|
||||
bool tx_init(MagSetting* setting) {
|
||||
// Initialize configured TX method
|
||||
switch(setting->tx) {
|
||||
case MagTxStateRFID:
|
||||
tx_init_rfid();
|
||||
break;
|
||||
case MagTxStateGPIO:
|
||||
// gpio_item_configure_all_pins(GpioModeOutputPushPull);
|
||||
furi_hal_gpio_init(GPIO_PIN_A, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(GPIO_PIN_B, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
furi_hal_gpio_write(GPIO_PIN_ENABLE, 1);
|
||||
|
||||
// had some issues with ~300; bumped higher temporarily
|
||||
furi_delay_ms(500);
|
||||
break;
|
||||
case MagTxStatePiezo:
|
||||
tx_init_piezo();
|
||||
break;
|
||||
case MagTxStateLF_P:
|
||||
tx_init_piezo();
|
||||
tx_init_rfid();
|
||||
break;
|
||||
case MagTxStateNFC:
|
||||
furi_hal_nfc_exit_sleep();
|
||||
break;
|
||||
case MagTxCC1101_434:
|
||||
tx_init_rf(434000000);
|
||||
break;
|
||||
case MagTxCC1101_868:
|
||||
tx_init_rf(868000000);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tx_deinit(MagSetting* setting) {
|
||||
// Reset configured TX method
|
||||
switch(setting->tx) {
|
||||
case MagTxStateRFID:
|
||||
tx_deinit_rfid();
|
||||
break;
|
||||
case MagTxStateGPIO:
|
||||
furi_hal_gpio_write(GPIO_PIN_A, 0);
|
||||
furi_hal_gpio_write(GPIO_PIN_B, 0);
|
||||
furi_hal_gpio_write(GPIO_PIN_ENABLE, 0);
|
||||
|
||||
// set back to analog output mode? - YES
|
||||
furi_hal_gpio_init(GPIO_PIN_A, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(GPIO_PIN_B, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
//gpio_item_configure_all_pins(GpioModeAnalog);
|
||||
break;
|
||||
case MagTxStatePiezo:
|
||||
tx_deinit_piezo();
|
||||
break;
|
||||
case MagTxStateLF_P:
|
||||
tx_deinit_piezo();
|
||||
tx_deinit_rfid();
|
||||
break;
|
||||
case MagTxStateNFC:
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_start_sleep();
|
||||
break;
|
||||
case MagTxCC1101_434:
|
||||
case MagTxCC1101_868:
|
||||
furi_hal_gpio_write(&gpio_cc1101_g0, false);
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_idle();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mag_spoof(Mag* mag) {
|
||||
MagSetting* setting = mag->setting;
|
||||
|
||||
// TODO: cleanup this section. Possibly move precompute + tx_init to emulate_on_enter?
|
||||
FuriString* ft1 = mag->mag_dev->dev_data.track[0].str;
|
||||
FuriString* ft2 = mag->mag_dev->dev_data.track[1].str;
|
||||
FuriString* ft3 = mag->mag_dev->dev_data.track[2].str;
|
||||
|
||||
char *data1, *data2, *data3;
|
||||
data1 = malloc(furi_string_size(ft1) + 1);
|
||||
data2 = malloc(furi_string_size(ft2) + 1);
|
||||
data3 = malloc(furi_string_size(ft3) + 1);
|
||||
strncpy(data1, furi_string_get_cstr(ft1), furi_string_size(ft1));
|
||||
strncpy(data2, furi_string_get_cstr(ft2), furi_string_size(ft2));
|
||||
strncpy(data3, furi_string_get_cstr(ft3), furi_string_size(ft3));
|
||||
|
||||
if(furi_log_get_level() >= FuriLogLevelDebug) {
|
||||
debug_mag_string(data1, bitlen[0], sublen[0]);
|
||||
debug_mag_string(data2, bitlen[1], sublen[1]);
|
||||
debug_mag_string(data3, bitlen[2], sublen[2]);
|
||||
}
|
||||
|
||||
uint8_t bits_t1_raw[64] = {0x00}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits
|
||||
uint8_t bits_t1_manchester[128] = {0x00}; // twice the above
|
||||
uint16_t bits_t1_count = mag_encode(
|
||||
data1, (uint8_t*)bits_t1_manchester, (uint8_t*)bits_t1_raw, bitlen[0], sublen[0]);
|
||||
uint8_t bits_t2_raw[64] = {0x00}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits
|
||||
uint8_t bits_t2_manchester[128] = {0x00}; // twice the above
|
||||
uint16_t bits_t2_count = mag_encode(
|
||||
data2, (uint8_t*)bits_t2_manchester, (uint8_t*)bits_t2_raw, bitlen[1], sublen[1]);
|
||||
uint8_t bits_t3_raw[64] = {0x00};
|
||||
uint8_t bits_t3_manchester[128] = {0x00};
|
||||
uint16_t bits_t3_count = mag_encode(
|
||||
data3, (uint8_t*)bits_t3_manchester, (uint8_t*)bits_t3_raw, bitlen[2], sublen[2]);
|
||||
|
||||
if(furi_log_get_level() >= FuriLogLevelDebug) {
|
||||
printf(
|
||||
"Manchester bitcount: T1: %d, T2: %d, T3: %d\r\n",
|
||||
bits_t1_count,
|
||||
bits_t2_count,
|
||||
bits_t3_count);
|
||||
printf("T1 raw: ");
|
||||
for(int i = 0; i < bits_t1_count / 16; i++) printf("%02x ", bits_t1_raw[i]);
|
||||
printf("\r\nT1 manchester: ");
|
||||
for(int i = 0; i < bits_t1_count / 8; i++) printf("%02x ", bits_t1_manchester[i]);
|
||||
printf("\r\nT2 raw: ");
|
||||
for(int i = 0; i < bits_t2_count / 16; i++) printf("%02x ", bits_t2_raw[i]);
|
||||
printf("\r\nT2 manchester: ");
|
||||
for(int i = 0; i < bits_t2_count / 8; i++) printf("%02x ", bits_t2_manchester[i]);
|
||||
printf("\r\nT3 raw: ");
|
||||
for(int i = 0; i < bits_t3_count / 16; i++) printf("%02x ", bits_t3_raw[i]);
|
||||
printf("\r\nT3 manchester: ");
|
||||
for(int i = 0; i < bits_t3_count / 8; i++) printf("%02x ", bits_t3_manchester[i]);
|
||||
printf("\r\nBitwise emulation done\r\n\r\n");
|
||||
}
|
||||
|
||||
last_value = 2;
|
||||
bool bit = false;
|
||||
|
||||
if(!tx_init(setting)) return;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
for(uint16_t i = 0; i < (ZERO_PREFIX * 2); i++) {
|
||||
// is this right?
|
||||
if(!!(i % 2)) bit ^= 1;
|
||||
play_halfbit(bit, setting);
|
||||
furi_delay_us(setting->us_clock);
|
||||
}
|
||||
|
||||
if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateOne))
|
||||
play_track((uint8_t*)bits_t1_manchester, bits_t1_count, setting, false);
|
||||
|
||||
if((setting->track == MagTrackStateOneAndTwo))
|
||||
for(uint16_t i = 0; i < (ZERO_BETWEEN * 2); i++) {
|
||||
if(!!(i % 2)) bit ^= 1;
|
||||
play_halfbit(bit, setting);
|
||||
furi_delay_us(setting->us_clock);
|
||||
}
|
||||
|
||||
if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateTwo))
|
||||
play_track(
|
||||
(uint8_t*)bits_t2_manchester,
|
||||
bits_t2_count,
|
||||
setting,
|
||||
(setting->reverse == MagReverseStateOn));
|
||||
|
||||
if((setting->track == MagTrackStateThree))
|
||||
play_track((uint8_t*)bits_t3_manchester, bits_t3_count, setting, false);
|
||||
|
||||
for(uint16_t i = 0; i < (ZERO_SUFFIX * 2); i++) {
|
||||
if(!!(i % 2)) bit ^= 1;
|
||||
play_halfbit(bit, setting);
|
||||
furi_delay_us(setting->us_clock);
|
||||
}
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
free(data1);
|
||||
free(data2);
|
||||
free(data3);
|
||||
tx_deinit(setting);
|
||||
}
|
||||
|
||||
uint16_t add_bit(bool value, uint8_t* out, uint16_t count) {
|
||||
uint8_t bit = count % 8;
|
||||
uint8_t byte = count / 8;
|
||||
if(value) {
|
||||
out[byte] |= 0x01;
|
||||
}
|
||||
if(bit < 7) out[byte] <<= 1;
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count) {
|
||||
static bool toggle = 0;
|
||||
toggle ^= 0x01;
|
||||
count = add_bit(toggle, out, count);
|
||||
if(value) toggle ^= 0x01;
|
||||
count = add_bit(toggle, out, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
uint16_t mag_encode(
|
||||
char* data,
|
||||
uint8_t* out_manchester,
|
||||
uint8_t* out_raw,
|
||||
uint8_t track_bits,
|
||||
uint8_t track_ascii_offset) {
|
||||
/*
|
||||
* track_bits - the number of raw (data) bits on the track. on ISO cards, that's 7 for track 1, or 5 for 2/3 - this is samy's bitlen
|
||||
* - this count includes the parity bit
|
||||
* track_ascii_offset - how much the ascii values are offset. track 1 makes space (ascii 32) become data 0x00,
|
||||
* - tracks 2/3 make ascii "0" become data 0x00 - this is samy's sublen
|
||||
*
|
||||
*/
|
||||
|
||||
uint16_t raw_bits_count = 0;
|
||||
uint16_t output_count = 0;
|
||||
int tmp, crc, lrc = 0;
|
||||
|
||||
/* // why are we adding zeros to the encoded string if we're also doing it while playing?
|
||||
for(int i = 0; i < ZERO_PREFIX; i++) {
|
||||
output_count = add_bit_manchester(0, out_manchester, output_count);
|
||||
raw_bits_count = add_bit(0, out_raw, raw_bits_count);
|
||||
}*/
|
||||
|
||||
for(int i = 0; *(data + i) != 0; i++) {
|
||||
crc = 1;
|
||||
tmp = *(data + i) - track_ascii_offset;
|
||||
|
||||
for(int j = 0; j < track_bits - 1; j++) {
|
||||
crc ^= tmp & 1;
|
||||
lrc ^= (tmp & 1) << j;
|
||||
raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count);
|
||||
output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count);
|
||||
tmp >>= 1;
|
||||
}
|
||||
raw_bits_count = add_bit(crc, out_raw, raw_bits_count);
|
||||
output_count = add_bit_manchester(crc, out_manchester, output_count);
|
||||
}
|
||||
|
||||
// LRC byte
|
||||
tmp = lrc;
|
||||
crc = 1;
|
||||
for(int j = 0; j < track_bits - 1; j++) {
|
||||
crc ^= tmp & 0x01;
|
||||
raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count);
|
||||
output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count);
|
||||
tmp >>= 1;
|
||||
}
|
||||
raw_bits_count = add_bit(crc, out_raw, raw_bits_count);
|
||||
output_count = add_bit_manchester(crc, out_manchester, output_count);
|
||||
|
||||
return output_count;
|
||||
}
|
||||
|
||||
void debug_mag_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset) {
|
||||
uint8_t bits_raw[64] = {0}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits
|
||||
uint8_t bits_manchester[128] = {0}; // twice the above
|
||||
int numbits = 0;
|
||||
|
||||
printf("Encoding [%s] with %d bits\r\n", data, track_bits);
|
||||
numbits = mag_encode(
|
||||
data, (uint8_t*)bits_manchester, (uint8_t*)bits_raw, track_bits, track_ascii_offset);
|
||||
printf("Got %d bits\r\n", numbits);
|
||||
printf("Raw byte stream: ");
|
||||
for(int i = 0; i < numbits / 8 / 2; i++) {
|
||||
printf("%02x", bits_raw[i]);
|
||||
if(i % 4 == 3) printf(" ");
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
|
||||
printf("Bits ");
|
||||
int space_counter = 0;
|
||||
for(int i = 0; i < numbits / 2; i++) {
|
||||
/*if(i < ZERO_PREFIX) {
|
||||
printf("X");
|
||||
continue;
|
||||
} else if(i == ZERO_PREFIX) {
|
||||
printf(" ");
|
||||
space_counter = 0;
|
||||
}*/
|
||||
printf("%01x", (bits_raw[i / 8] & (1 << (7 - (i % 8)))) != 0);
|
||||
if((space_counter) % track_bits == track_bits - 1) printf(" ");
|
||||
space_counter++;
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
|
||||
printf("Manchester encoded, byte stream: ");
|
||||
for(int i = 0; i < numbits / 8; i++) {
|
||||
printf("%02x", bits_manchester[i]);
|
||||
if(i % 4 == 3) printf(" ");
|
||||
}
|
||||
printf("\r\n\r\n");
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void play_halfbit(bool value, MagSetting* setting);
|
||||
void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse);
|
||||
|
||||
void tx_init_rf(int hz);
|
||||
void tx_init_rfid();
|
||||
void tx_init_piezo();
|
||||
bool tx_init(MagSetting* setting);
|
||||
void tx_deinit_piezo();
|
||||
void tx_deinit_rfid();
|
||||
bool tx_deinit(MagSetting* setting);
|
||||
|
||||
uint16_t add_bit(bool value, uint8_t* out, uint16_t count);
|
||||
uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count);
|
||||
uint16_t mag_encode(
|
||||
char* data,
|
||||
uint8_t* out_manchester,
|
||||
uint8_t* out_raw,
|
||||
uint8_t track_bits,
|
||||
uint8_t track_ascii_offset);
|
||||
void debug_mag_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset);
|
||||
void mag_spoof(Mag* mag);
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define MAG_VERSION_APP "0.05"
|
||||
#define MAG_DEVELOPER "Zachary Weiss"
|
||||
#define MAG_GITHUB "github.com/zacharyweiss/magspoof_flipper"
|
||||
|
||||
typedef enum {
|
||||
MagViewSubmenu,
|
||||
MagViewDialogEx,
|
||||
MagViewPopup,
|
||||
MagViewLoading,
|
||||
MagViewWidget,
|
||||
MagViewVariableItemList,
|
||||
MagViewTextInput,
|
||||
} MagView;
|
||||
|
||||
typedef enum {
|
||||
MagReverseStateOff,
|
||||
MagReverseStateOn,
|
||||
} MagReverseState;
|
||||
|
||||
typedef enum {
|
||||
MagTrackStateOneAndTwo,
|
||||
MagTrackStateOne,
|
||||
MagTrackStateTwo,
|
||||
MagTrackStateThree,
|
||||
} MagTrackState;
|
||||
|
||||
typedef enum {
|
||||
MagTxStateRFID,
|
||||
MagTxStateGPIO,
|
||||
MagTxStatePiezo,
|
||||
MagTxStateLF_P, // combo of RFID and Piezo
|
||||
MagTxStateNFC,
|
||||
MagTxCC1101_434,
|
||||
MagTxCC1101_868,
|
||||
} MagTxState;
|
||||
|
||||
typedef enum {
|
||||
UART_TerminalEventRefreshConsoleOutput = 0,
|
||||
UART_TerminalEventStartConsole,
|
||||
UART_TerminalEventStartKeyboard,
|
||||
} UART_TerminalCustomEvent;
|
||||
BIN
applications/external/magspoof/icons/mag_10px.png
vendored
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
248
applications/external/magspoof/mag.c
vendored
@@ -1,248 +0,0 @@
|
||||
#include "mag_i.h"
|
||||
|
||||
#define TAG "Mag"
|
||||
|
||||
#define SETTING_DEFAULT_REVERSE MagReverseStateOff
|
||||
#define SETTING_DEFAULT_TRACK MagTrackStateOneAndTwo
|
||||
#define SETTING_DEFAULT_TX_RFID MagTxStateGPIO
|
||||
#define SETTING_DEFAULT_US_CLOCK 240
|
||||
#define SETTING_DEFAULT_US_INTERPACKET 10
|
||||
|
||||
static bool mag_debug_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
Mag* mag = context;
|
||||
return scene_manager_handle_custom_event(mag->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool mag_debug_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Mag* mag = context;
|
||||
return scene_manager_handle_back_event(mag->scene_manager);
|
||||
}
|
||||
|
||||
static MagSetting* mag_setting_alloc() {
|
||||
// temp hardcoded defaults
|
||||
MagSetting* setting = malloc(sizeof(MagSetting));
|
||||
setting->reverse = SETTING_DEFAULT_REVERSE;
|
||||
setting->track = SETTING_DEFAULT_TRACK;
|
||||
setting->tx = SETTING_DEFAULT_TX_RFID;
|
||||
setting->us_clock = SETTING_DEFAULT_US_CLOCK;
|
||||
setting->us_interpacket = SETTING_DEFAULT_US_INTERPACKET;
|
||||
|
||||
return setting;
|
||||
}
|
||||
|
||||
static Mag* mag_alloc() {
|
||||
Mag* mag = malloc(sizeof(Mag));
|
||||
|
||||
mag->storage = furi_record_open(RECORD_STORAGE);
|
||||
mag->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
mag->file_name = furi_string_alloc();
|
||||
mag->file_path = furi_string_alloc_set(MAG_APP_FOLDER);
|
||||
|
||||
mag->view_dispatcher = view_dispatcher_alloc();
|
||||
mag->scene_manager = scene_manager_alloc(&mag_scene_handlers, mag);
|
||||
view_dispatcher_enable_queue(mag->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(mag->view_dispatcher, mag);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
mag->view_dispatcher, mag_debug_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
mag->view_dispatcher, mag_debug_back_event_callback);
|
||||
|
||||
mag->mag_dev = mag_device_alloc();
|
||||
mag->setting = mag_setting_alloc();
|
||||
|
||||
// Open GUI record
|
||||
mag->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// Open Notification record
|
||||
mag->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// Submenu
|
||||
mag->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(mag->view_dispatcher, MagViewSubmenu, submenu_get_view(mag->submenu));
|
||||
|
||||
// Dialog
|
||||
mag->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
mag->view_dispatcher, MagViewDialogEx, dialog_ex_get_view(mag->dialog_ex));
|
||||
|
||||
// Popup
|
||||
mag->popup = popup_alloc();
|
||||
view_dispatcher_add_view(mag->view_dispatcher, MagViewPopup, popup_get_view(mag->popup));
|
||||
|
||||
// Loading
|
||||
mag->loading = loading_alloc();
|
||||
view_dispatcher_add_view(mag->view_dispatcher, MagViewLoading, loading_get_view(mag->loading));
|
||||
|
||||
// Widget
|
||||
mag->widget = widget_alloc();
|
||||
view_dispatcher_add_view(mag->view_dispatcher, MagViewWidget, widget_get_view(mag->widget));
|
||||
|
||||
// Variable Item List
|
||||
mag->variable_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
mag->view_dispatcher,
|
||||
MagViewVariableItemList,
|
||||
variable_item_list_get_view(mag->variable_item_list));
|
||||
|
||||
// Text Input
|
||||
mag->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
mag->view_dispatcher, MagViewTextInput, text_input_get_view(mag->text_input));
|
||||
|
||||
return mag;
|
||||
}
|
||||
|
||||
static void mag_setting_free(MagSetting* setting) {
|
||||
furi_assert(setting);
|
||||
|
||||
free(setting);
|
||||
}
|
||||
|
||||
static void mag_free(Mag* mag) {
|
||||
furi_assert(mag);
|
||||
|
||||
furi_string_free(mag->file_name);
|
||||
furi_string_free(mag->file_path);
|
||||
|
||||
// Mag device
|
||||
mag_device_free(mag->mag_dev);
|
||||
mag->mag_dev = NULL;
|
||||
|
||||
// Mag setting
|
||||
mag_setting_free(mag->setting);
|
||||
mag->setting = NULL;
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewSubmenu);
|
||||
submenu_free(mag->submenu);
|
||||
|
||||
// DialogEx
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewDialogEx);
|
||||
dialog_ex_free(mag->dialog_ex);
|
||||
|
||||
// Popup
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewPopup);
|
||||
popup_free(mag->popup);
|
||||
|
||||
// Loading
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewLoading);
|
||||
loading_free(mag->loading);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewWidget);
|
||||
widget_free(mag->widget);
|
||||
|
||||
// Variable Item List
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewVariableItemList);
|
||||
variable_item_list_free(mag->variable_item_list);
|
||||
|
||||
// TextInput
|
||||
view_dispatcher_remove_view(mag->view_dispatcher, MagViewTextInput);
|
||||
text_input_free(mag->text_input);
|
||||
|
||||
// View Dispatcher
|
||||
view_dispatcher_free(mag->view_dispatcher);
|
||||
|
||||
// Scene Manager
|
||||
scene_manager_free(mag->scene_manager);
|
||||
|
||||
// GUI
|
||||
furi_record_close(RECORD_GUI);
|
||||
mag->gui = NULL;
|
||||
|
||||
// Notifications
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
mag->notifications = NULL;
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
free(mag);
|
||||
}
|
||||
|
||||
// entry point for app
|
||||
int32_t mag_app(void* p) {
|
||||
Mag* mag = mag_alloc();
|
||||
UNUSED(p);
|
||||
|
||||
mag_make_app_folder(mag);
|
||||
|
||||
// Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
|
||||
uint8_t attempts = 0;
|
||||
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
|
||||
view_dispatcher_attach_to_gui(mag->view_dispatcher, mag->gui, ViewDispatcherTypeFullscreen);
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneStart);
|
||||
|
||||
view_dispatcher_run(mag->view_dispatcher);
|
||||
|
||||
// Disable 5v power
|
||||
if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
mag_free(mag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mag_make_app_folder(Mag* mag) {
|
||||
furi_assert(mag);
|
||||
|
||||
if(!storage_simply_mkdir(mag->storage, MAG_APP_FOLDER)) {
|
||||
dialog_message_show_storage_error(mag->dialogs, "Cannot create\napp folder");
|
||||
}
|
||||
}
|
||||
|
||||
void mag_text_store_set(Mag* mag, const char* text, ...) {
|
||||
furi_assert(mag);
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(mag->text_store, MAG_TEXT_STORE_SIZE, text, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void mag_text_store_clear(Mag* mag) {
|
||||
furi_assert(mag);
|
||||
memset(mag->text_store, 0, sizeof(mag->text_store));
|
||||
}
|
||||
|
||||
void mag_popup_timeout_callback(void* context) {
|
||||
Mag* mag = context;
|
||||
view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventPopupClosed);
|
||||
}
|
||||
|
||||
void mag_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
Mag* mag = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(mag->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void mag_text_input_callback(void* context) {
|
||||
Mag* mag = context;
|
||||
view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventNext);
|
||||
}
|
||||
|
||||
void mag_show_loading_popup(void* context, bool show) {
|
||||
Mag* mag = context;
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
|
||||
if(show) {
|
||||
// Raise timer priority so that animations can play
|
||||
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewLoading);
|
||||
} else {
|
||||
// Restore default timer priority
|
||||
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
|
||||
}
|
||||
}
|
||||
311
applications/external/magspoof/mag_device.c
vendored
@@ -1,311 +0,0 @@
|
||||
#include "mag_device.h"
|
||||
|
||||
#include <toolbox/path.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define TAG "MagDevice"
|
||||
|
||||
static const char* mag_file_header = "Flipper Mag device";
|
||||
static const uint32_t mag_file_version = 1;
|
||||
|
||||
MagDevice* mag_device_alloc() {
|
||||
MagDevice* mag_dev = malloc(sizeof(MagDevice));
|
||||
mag_dev->dev_data.track[0].str = furi_string_alloc();
|
||||
mag_dev->dev_data.track[1].str = furi_string_alloc();
|
||||
mag_dev->dev_data.track[2].str = furi_string_alloc();
|
||||
mag_dev->storage = furi_record_open(RECORD_STORAGE);
|
||||
mag_dev->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
mag_dev->load_path = furi_string_alloc();
|
||||
return mag_dev;
|
||||
}
|
||||
|
||||
void mag_device_data_clear(MagDeviceData* dev_data) {
|
||||
furi_string_reset(dev_data->track[0].str);
|
||||
furi_string_reset(dev_data->track[1].str);
|
||||
furi_string_reset(dev_data->track[2].str);
|
||||
}
|
||||
|
||||
void mag_device_clear(MagDevice* mag_dev) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
mag_device_data_clear(&mag_dev->dev_data);
|
||||
memset(&mag_dev->dev_data, 0, sizeof(mag_dev->dev_data));
|
||||
furi_string_reset(mag_dev->load_path);
|
||||
}
|
||||
|
||||
void mag_device_free(MagDevice* mag_dev) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
mag_device_clear(mag_dev);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_string_free(mag_dev->load_path);
|
||||
|
||||
//furi_string_free(mag_dev->dev_data.track[0].str);
|
||||
//furi_string_free(mag_dev->dev_data.track[1].str);
|
||||
//furi_string_free(mag_dev->dev_data.track[2].str);
|
||||
|
||||
free(mag_dev);
|
||||
}
|
||||
|
||||
void mag_device_set_name(MagDevice* mag_dev, const char* name) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
strlcpy(mag_dev->dev_name, name, MAG_DEV_NAME_MAX_LEN);
|
||||
}
|
||||
|
||||
static bool mag_device_save_file(
|
||||
MagDevice* mag_dev,
|
||||
const char* dev_name,
|
||||
const char* folder,
|
||||
const char* extension,
|
||||
bool use_load_path) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
bool saved = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(mag_dev->storage);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(use_load_path && !furi_string_empty(mag_dev->load_path)) {
|
||||
// Get dir name
|
||||
path_extract_dirname(furi_string_get_cstr(mag_dev->load_path), temp_str);
|
||||
// Create mag directory if necessary
|
||||
if(!storage_simply_mkdir((mag_dev->storage), furi_string_get_cstr(temp_str))) break;
|
||||
// Make path to file to be saved
|
||||
furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension);
|
||||
} else {
|
||||
// Create mag directory if necessary
|
||||
if(!storage_simply_mkdir((mag_dev->storage), MAG_APP_FOLDER)) break;
|
||||
// First remove mag device file if it was saved
|
||||
furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
|
||||
}
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
|
||||
|
||||
// Write header
|
||||
if(!flipper_format_write_header_cstr(file, mag_file_header, mag_file_version)) break;
|
||||
|
||||
// Write comment
|
||||
if(!flipper_format_write_comment_cstr(file, "Mag device track data")) break;
|
||||
|
||||
// Write data
|
||||
for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) {
|
||||
furi_string_printf(temp_str, "Track %d", i + 1);
|
||||
if(!flipper_format_write_string_cstr(
|
||||
file,
|
||||
furi_string_get_cstr(temp_str),
|
||||
furi_string_get_cstr(mag_dev->dev_data.track[i].str)))
|
||||
break;
|
||||
}
|
||||
|
||||
saved = true;
|
||||
} while(0);
|
||||
|
||||
if(!saved) {
|
||||
dialog_message_show_storage_error(mag_dev->dialogs, "Cannot save\nfile");
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(file);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool mag_device_save(MagDevice* mag_dev, const char* dev_name) {
|
||||
// wrapping function in the event we have multiple formats
|
||||
return mag_device_save_file(mag_dev, dev_name, MAG_APP_FOLDER, MAG_APP_EXTENSION, true);
|
||||
}
|
||||
|
||||
static bool mag_device_load_data(MagDevice* mag_dev, FuriString* path, bool show_dialog) {
|
||||
bool parsed = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(mag_dev->storage);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
bool deprecated_version = false;
|
||||
bool data_read = true;
|
||||
|
||||
if(mag_dev->loading_cb) {
|
||||
mag_dev->loading_cb(mag_dev->loading_cb_ctx, true);
|
||||
}
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
|
||||
|
||||
// Read and verify header, check file version
|
||||
uint32_t version;
|
||||
if(!flipper_format_read_header(file, temp_str, &version)) break;
|
||||
if(furi_string_cmp_str(temp_str, mag_file_header) || (version != mag_file_version)) {
|
||||
deprecated_version = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse data
|
||||
for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) {
|
||||
furi_string_printf(temp_str, "Track %d", i + 1);
|
||||
if(!flipper_format_read_string(
|
||||
file, furi_string_get_cstr(temp_str), mag_dev->dev_data.track[i].str)) {
|
||||
FURI_LOG_D(TAG, "Could not read track %d data", i + 1);
|
||||
|
||||
// TODO: smarter load handling now that it is acceptible for some tracks to be empty
|
||||
data_read = false;
|
||||
}
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
if((!parsed) && (show_dialog)) {
|
||||
if(deprecated_version) {
|
||||
dialog_message_show_storage_error(mag_dev->dialogs, "File format\ndeprecated");
|
||||
} else if(!data_read) {
|
||||
dialog_message_show_storage_error(mag_dev->dialogs, "Cannot read\ndata");
|
||||
} else {
|
||||
dialog_message_show_storage_error(mag_dev->dialogs, "Cannot parse\nfile");
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(file);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool mag_file_select(MagDevice* mag_dev) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
FuriString* mag_app_folder;
|
||||
mag_app_folder = furi_string_alloc_set(MAG_APP_FOLDER);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, MAG_APP_EXTENSION, &I_mag_file_10px);
|
||||
browser_options.base_path = MAG_APP_FOLDER;
|
||||
|
||||
bool res = dialog_file_browser_show(
|
||||
mag_dev->dialogs, mag_dev->load_path, mag_app_folder, &browser_options);
|
||||
|
||||
furi_string_free(mag_app_folder);
|
||||
if(res) {
|
||||
FuriString* filename;
|
||||
filename = furi_string_alloc();
|
||||
path_extract_filename(mag_dev->load_path, filename, true);
|
||||
strncpy(mag_dev->dev_name, furi_string_get_cstr(filename), MAG_DEV_NAME_MAX_LEN);
|
||||
res = mag_device_load_data(mag_dev, mag_dev->load_path, true);
|
||||
if(res) {
|
||||
mag_device_set_name(mag_dev, mag_dev->dev_name);
|
||||
}
|
||||
furi_string_free(filename);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool mag_device_delete(MagDevice* mag_dev, bool use_load_path) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
bool deleted = false;
|
||||
FuriString* file_path;
|
||||
file_path = furi_string_alloc();
|
||||
|
||||
do {
|
||||
// Delete original file
|
||||
if(use_load_path && !furi_string_empty(mag_dev->load_path)) {
|
||||
furi_string_set(file_path, mag_dev->load_path);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
file_path, "%s/%s%s", MAG_APP_FOLDER, mag_dev->dev_name, MAG_APP_EXTENSION);
|
||||
}
|
||||
if(!storage_simply_remove(mag_dev->storage, furi_string_get_cstr(file_path))) break;
|
||||
deleted = true;
|
||||
} while(false);
|
||||
|
||||
if(!deleted) {
|
||||
dialog_message_show_storage_error(mag_dev->dialogs, "Cannot remove\nfile");
|
||||
}
|
||||
|
||||
furi_string_free(file_path);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
bool mag_device_parse_card_string(MagDevice* mag_dev, FuriString* f_card_str) {
|
||||
furi_assert(mag_dev);
|
||||
FURI_LOG_D(TAG, "mag_device_parse_card_string");
|
||||
|
||||
const char* card_str = furi_string_get_cstr(f_card_str);
|
||||
|
||||
FURI_LOG_D(TAG, "Parsing card string: %s", card_str);
|
||||
|
||||
// Track 1
|
||||
const char* track1_start = strchr(card_str, '%');
|
||||
if(!track1_start) {
|
||||
FURI_LOG_D(TAG, "Could not find track 1 start");
|
||||
return false;
|
||||
}
|
||||
track1_start++;
|
||||
const char* track1_end = strchr(track1_start, '?');
|
||||
if(!track1_end) {
|
||||
FURI_LOG_D(TAG, "Could not find track 1 end");
|
||||
return false;
|
||||
}
|
||||
size_t track1_len = track1_end - track1_start;
|
||||
|
||||
FURI_LOG_D(TAG, "Track 1: %.*s", track1_len, track1_start);
|
||||
|
||||
mag_dev->dev_data.track[0].len = track1_len;
|
||||
furi_string_printf(mag_dev->dev_data.track[0].str, "%%%.*s?", track1_len, track1_start);
|
||||
|
||||
// Track 2
|
||||
const char* track2_start = strchr(track1_end, ';');
|
||||
if(!track2_start) {
|
||||
FURI_LOG_D(TAG, "Could not find track 2 start");
|
||||
return true;
|
||||
}
|
||||
|
||||
track2_start++;
|
||||
const char* track2_end = strchr(track2_start, '?');
|
||||
if(!track2_end) {
|
||||
FURI_LOG_D(TAG, "Could not find track 2 end");
|
||||
return true;
|
||||
}
|
||||
size_t track2_len = track2_end - track2_start;
|
||||
|
||||
FURI_LOG_D(TAG, "Track 2: %.*s", track2_len, track2_start);
|
||||
|
||||
mag_dev->dev_data.track[1].len = track2_len;
|
||||
furi_string_printf(mag_dev->dev_data.track[1].str, "%%%.*s?", track2_len, track2_start);
|
||||
|
||||
// Track 3
|
||||
const char* track3_start = strchr(track2_end, ';');
|
||||
if(!track3_start) {
|
||||
FURI_LOG_D(TAG, "Could not find track 3 start");
|
||||
return true;
|
||||
}
|
||||
|
||||
track3_start++;
|
||||
const char* track3_end = strchr(track3_start, '?');
|
||||
if(!track3_end) {
|
||||
FURI_LOG_D(TAG, "Could not find track 3 end");
|
||||
return true;
|
||||
}
|
||||
size_t track3_len = track3_end - track3_start;
|
||||
|
||||
FURI_LOG_D(TAG, "Track 3: %.*s", track3_len, track3_start);
|
||||
|
||||
mag_dev->dev_data.track[2].len = track3_len;
|
||||
furi_string_printf(mag_dev->dev_data.track[2].str, "%%%.*s?", track3_len, track3_start);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mag_device_set_loading_callback(
|
||||
MagDevice* mag_dev,
|
||||
MagLoadingCallback callback,
|
||||
void* context) {
|
||||
furi_assert(mag_dev);
|
||||
|
||||
mag_dev->loading_cb = callback;
|
||||
mag_dev->loading_cb_ctx = context;
|
||||
}
|
||||
59
applications/external/magspoof/mag_device.h
vendored
@@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "magspoof_icons.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define MAG_DEV_NAME_MAX_LEN 22
|
||||
#define MAG_DEV_TRACKS 3
|
||||
|
||||
#define MAG_APP_FOLDER STORAGE_APP_DATA_PATH_PREFIX
|
||||
#define MAG_APP_EXTENSION ".mag"
|
||||
|
||||
typedef void (*MagLoadingCallback)(void* context, bool state);
|
||||
|
||||
typedef struct {
|
||||
FuriString* str;
|
||||
size_t len;
|
||||
} MagTrack;
|
||||
|
||||
typedef struct {
|
||||
MagTrack track[MAG_DEV_TRACKS];
|
||||
} MagDeviceData;
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
DialogsApp* dialogs;
|
||||
MagDeviceData dev_data;
|
||||
char dev_name[MAG_DEV_NAME_MAX_LEN + 1];
|
||||
FuriString* load_path;
|
||||
MagLoadingCallback loading_cb;
|
||||
void* loading_cb_ctx;
|
||||
} MagDevice;
|
||||
|
||||
MagDevice* mag_device_alloc();
|
||||
|
||||
void mag_device_free(MagDevice* mag_dev);
|
||||
|
||||
void mag_device_set_name(MagDevice* mag_dev, const char* name);
|
||||
|
||||
bool mag_device_save(MagDevice* mag_dev, const char* dev_name);
|
||||
|
||||
bool mag_file_select(MagDevice* mag_dev);
|
||||
|
||||
void mag_device_data_clear(MagDeviceData* dev_data);
|
||||
|
||||
void mag_device_clear(MagDevice* mag_dev);
|
||||
|
||||
bool mag_device_delete(MagDevice* mag_dev, bool use_load_path);
|
||||
|
||||
bool mag_device_parse_card_string(MagDevice* mag_dev, FuriString* card_str);
|
||||
|
||||
void mag_device_set_loading_callback(
|
||||
MagDevice* mag_dev,
|
||||
MagLoadingCallback callback,
|
||||
void* context);
|
||||
101
applications/external/magspoof/mag_i.h
vendored
@@ -1,101 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "mag_device.h"
|
||||
//#include "helpers/mag_helpers.h"
|
||||
#include "helpers/mag_types.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi/core/log.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <storage/storage.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#include <toolbox/path.h>
|
||||
#include <toolbox/value_index.h>
|
||||
|
||||
#include "scenes/mag_scene.h"
|
||||
#include "scenes/mag_scene_read.h"
|
||||
|
||||
#define MAG_TEXT_STORE_SIZE 150
|
||||
|
||||
enum MagCustomEvent {
|
||||
MagEventNext = 100,
|
||||
MagEventExit,
|
||||
MagEventPopupClosed,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
MagTxState tx;
|
||||
MagTrackState track;
|
||||
MagReverseState reverse;
|
||||
uint32_t us_clock;
|
||||
uint32_t us_interpacket;
|
||||
} MagSetting;
|
||||
|
||||
typedef struct {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
SceneManager* scene_manager;
|
||||
Storage* storage;
|
||||
DialogsApp* dialogs;
|
||||
MagDevice* mag_dev;
|
||||
|
||||
char text_store[MAG_TEXT_STORE_SIZE + 1];
|
||||
FuriString* file_path;
|
||||
FuriString* file_name;
|
||||
|
||||
MagSetting* setting;
|
||||
|
||||
// Common views
|
||||
Submenu* submenu;
|
||||
DialogEx* dialog_ex;
|
||||
Popup* popup;
|
||||
Loading* loading;
|
||||
TextInput* text_input;
|
||||
Widget* widget;
|
||||
VariableItemList* variable_item_list;
|
||||
|
||||
// UART
|
||||
FuriThread* uart_rx_thread;
|
||||
FuriStreamBuffer* uart_rx_stream;
|
||||
uint8_t uart_rx_buf[UART_RX_BUF_SIZE + 1];
|
||||
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context);
|
||||
|
||||
char uart_text_input_store[UART_TERMINAL_TEXT_INPUT_STORE_SIZE + 1];
|
||||
FuriString* uart_text_box_store;
|
||||
size_t uart_text_box_store_strlen;
|
||||
// UART_TextInput* text_input;
|
||||
} Mag;
|
||||
|
||||
void mag_text_store_set(Mag* mag, const char* text, ...);
|
||||
|
||||
void mag_text_store_clear(Mag* mag);
|
||||
|
||||
void mag_show_loading_popup(void* context, bool show);
|
||||
|
||||
void mag_make_app_folder(Mag* mag);
|
||||
|
||||
void mag_popup_timeout_callback(void* context);
|
||||
|
||||
void mag_widget_callback(GuiButtonType result, InputType type, void* context);
|
||||
|
||||
void mag_text_input_callback(void* context);
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "mag_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const mag_on_enter_handlers[])(void*) = {
|
||||
#include "mag_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const mag_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "mag_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const mag_on_exit_handlers[])(void* context) = {
|
||||
#include "mag_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers mag_scene_handlers = {
|
||||
.on_enter_handlers = mag_on_enter_handlers,
|
||||
.on_event_handlers = mag_on_event_handlers,
|
||||
.on_exit_handlers = mag_on_exit_handlers,
|
||||
.scene_num = MagSceneNum,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) MagScene##id,
|
||||
typedef enum {
|
||||
#include "mag_scene_config.h"
|
||||
MagSceneNum,
|
||||
} MagScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers mag_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "mag_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "mag_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "mag_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,40 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_about_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Widget* widget = mag->widget;
|
||||
|
||||
FuriString* tmp_str;
|
||||
tmp_str = furi_string_alloc();
|
||||
|
||||
furi_string_cat_printf(tmp_str, "Version: %s\n", MAG_VERSION_APP);
|
||||
furi_string_cat_printf(tmp_str, "Developer: %s\n", MAG_DEVELOPER);
|
||||
furi_string_cat_printf(tmp_str, "GitHub: %s\n\n", MAG_GITHUB);
|
||||
|
||||
furi_string_cat_printf(
|
||||
tmp_str,
|
||||
"Unfinished port of Samy Kamkar's MagSpoof. Confer GitHub for updates; in the interim, use responsibly and at your own risk.");
|
||||
|
||||
// TODO: Add credits
|
||||
|
||||
widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp_str));
|
||||
furi_string_free(tmp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
||||
}
|
||||
|
||||
bool mag_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
UNUSED(event);
|
||||
UNUSED(scene_manager);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_about_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
widget_reset(mag->widget);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
ADD_SCENE(mag, start, Start)
|
||||
ADD_SCENE(mag, about, About)
|
||||
ADD_SCENE(mag, emulate, Emulate)
|
||||
ADD_SCENE(mag, emulate_config, EmulateConfig)
|
||||
ADD_SCENE(mag, file_select, FileSelect)
|
||||
ADD_SCENE(mag, saved_menu, SavedMenu)
|
||||
ADD_SCENE(mag, saved_info, SavedInfo)
|
||||
ADD_SCENE(mag, input_name, InputName)
|
||||
ADD_SCENE(mag, input_value, InputValue)
|
||||
ADD_SCENE(mag, save_success, SaveSuccess)
|
||||
ADD_SCENE(mag, delete_success, DeleteSuccess)
|
||||
ADD_SCENE(mag, delete_confirm, DeleteConfirm)
|
||||
ADD_SCENE(mag, exit_confirm, ExitConfirm)
|
||||
ADD_SCENE(mag, under_construction, UnderConstruction)
|
||||
ADD_SCENE(mag, read, Read)
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
#include "../mag_device.h"
|
||||
|
||||
void mag_scene_delete_confirm_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Widget* widget = mag->widget;
|
||||
MagDevice* mag_dev = mag->mag_dev;
|
||||
|
||||
FuriString* tmp_str;
|
||||
tmp_str = furi_string_alloc();
|
||||
|
||||
furi_string_printf(tmp_str, "\e#Delete %s?\e#", mag_dev->dev_name);
|
||||
|
||||
//TODO: print concise summary of data on card? Would need to vary by card/track type
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(tmp_str), true);
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", mag_widget_callback, mag);
|
||||
widget_add_button_element(widget, GuiButtonTypeRight, "Delete", mag_widget_callback, mag);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
||||
|
||||
furi_string_free(tmp_str);
|
||||
}
|
||||
|
||||
bool mag_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
consumed = true;
|
||||
if(mag_device_delete(mag->mag_dev, true)) {
|
||||
scene_manager_next_scene(scene_manager, MagSceneDeleteSuccess);
|
||||
}
|
||||
} else if(event.event == GuiButtonTypeLeft) {
|
||||
consumed = true;
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_delete_confirm_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
widget_reset(mag->widget);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_delete_success_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Popup* popup = mag->popup;
|
||||
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
popup_set_callback(popup, mag_popup_timeout_callback);
|
||||
popup_set_context(popup, mag);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewPopup);
|
||||
}
|
||||
|
||||
bool mag_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == MagEventPopupClosed) {
|
||||
consumed = true;
|
||||
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
mag->scene_manager, MagSceneFileSelect);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_delete_success_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
Popup* popup = mag->popup;
|
||||
|
||||
popup_reset(popup);
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
#include "../helpers/mag_helpers.h"
|
||||
|
||||
#define TAG "MagSceneEmulate"
|
||||
|
||||
void cat_trackstr(FuriString* str, uint8_t calls, uint8_t i, FuriString* trackstr) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"%sTrack %d:%s%s\n",
|
||||
(calls == 0) ? "" : "\n", // if first line, don't prepend a "\n"
|
||||
(i + 1),
|
||||
furi_string_empty(trackstr) ? " " : "\n",
|
||||
furi_string_empty(trackstr) ? "< empty >" : furi_string_get_cstr(trackstr));
|
||||
}
|
||||
|
||||
void mag_scene_emulate_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Widget* widget = mag->widget;
|
||||
|
||||
FuriString* tmp_str;
|
||||
tmp_str = furi_string_alloc();
|
||||
|
||||
// Use strlcpy instead perhaps, to truncate to screen width, then add ellipses if needed?
|
||||
furi_string_printf(tmp_str, "%s\r\n", mag->mag_dev->dev_name);
|
||||
|
||||
// TODO: Display other relevant config settings (namely RFID vs GPIO)?
|
||||
|
||||
widget_add_icon_element(widget, 1, 1, &I_mag_file_10px);
|
||||
widget_add_string_element(
|
||||
widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str));
|
||||
furi_string_reset(tmp_str);
|
||||
|
||||
FURI_LOG_D(TAG, "%d", mag->setting->reverse);
|
||||
|
||||
// print relevant data
|
||||
uint8_t cat_count = 0;
|
||||
for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) {
|
||||
FuriString* trackstr = mag->mag_dev->dev_data.track[i].str;
|
||||
|
||||
// still messy / dumb way to do this, but slightly cleaner than before.
|
||||
// will clean up more later
|
||||
switch(mag->setting->track) {
|
||||
case MagTrackStateOne:
|
||||
if(i == 0) cat_trackstr(tmp_str, cat_count++, i, trackstr);
|
||||
break;
|
||||
case MagTrackStateTwo:
|
||||
if(i == 1) cat_trackstr(tmp_str, cat_count++, i, trackstr);
|
||||
break;
|
||||
case MagTrackStateThree:
|
||||
if(i == 2) cat_trackstr(tmp_str, cat_count++, i, trackstr);
|
||||
break;
|
||||
case MagTrackStateOneAndTwo:
|
||||
if((i == 0) | (i == 1)) cat_trackstr(tmp_str, cat_count++, i, trackstr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
widget_add_text_scroll_element(widget, 0, 15, 128, 49, furi_string_get_cstr(tmp_str));
|
||||
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Config", mag_widget_callback, mag);
|
||||
widget_add_button_element(widget, GuiButtonTypeRight, "Send", mag_widget_callback, mag);
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
||||
furi_string_free(tmp_str);
|
||||
}
|
||||
|
||||
bool mag_scene_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case GuiButtonTypeLeft:
|
||||
consumed = true;
|
||||
scene_manager_next_scene(scene_manager, MagSceneEmulateConfig);
|
||||
break;
|
||||
case GuiButtonTypeRight:
|
||||
consumed = true;
|
||||
notification_message(mag->notifications, &sequence_blink_start_cyan);
|
||||
mag_spoof(mag);
|
||||
notification_message(mag->notifications, &sequence_blink_stop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_emulate_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
notification_message(mag->notifications, &sequence_blink_stop);
|
||||
widget_reset(mag->widget);
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
#define TAG "MagSceneEmulateConfig"
|
||||
|
||||
enum MagSettingIndex {
|
||||
MagSettingIndexTx,
|
||||
MagSettingIndexTrack,
|
||||
MagSettingIndexReverse,
|
||||
MagSettingIndexClock,
|
||||
MagSettingIndexInterpacket,
|
||||
};
|
||||
|
||||
#define TX_COUNT 7
|
||||
const char* const tx_text[TX_COUNT] = {
|
||||
"RFID",
|
||||
"GPIO",
|
||||
"Piezo",
|
||||
"LF + P",
|
||||
"NFC",
|
||||
"434MHz",
|
||||
"868MHz",
|
||||
};
|
||||
const uint32_t tx_value[TX_COUNT] = {
|
||||
MagTxStateRFID,
|
||||
MagTxStateGPIO,
|
||||
MagTxStatePiezo,
|
||||
MagTxStateLF_P,
|
||||
MagTxStateNFC,
|
||||
MagTxCC1101_434,
|
||||
MagTxCC1101_868,
|
||||
};
|
||||
|
||||
#define TRACK_COUNT 4
|
||||
const char* const track_text[TRACK_COUNT] = {
|
||||
"1 + 2",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
};
|
||||
const uint32_t track_value[TRACK_COUNT] = {
|
||||
MagTrackStateOneAndTwo,
|
||||
MagTrackStateOne,
|
||||
MagTrackStateTwo,
|
||||
MagTrackStateThree,
|
||||
};
|
||||
|
||||
#define REVERSE_COUNT 2
|
||||
const char* const reverse_text[REVERSE_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t reverse_value[REVERSE_COUNT] = {
|
||||
MagReverseStateOff,
|
||||
MagReverseStateOn,
|
||||
};
|
||||
|
||||
#define CLOCK_COUNT 15
|
||||
const char* const clock_text[CLOCK_COUNT] = {
|
||||
"200us",
|
||||
"220us",
|
||||
"240us",
|
||||
"250us",
|
||||
"260us",
|
||||
"280us",
|
||||
"300us",
|
||||
"325us",
|
||||
"350us",
|
||||
"375us",
|
||||
"400us",
|
||||
"450us",
|
||||
"500us",
|
||||
"600us",
|
||||
"700us",
|
||||
};
|
||||
const uint32_t clock_value[CLOCK_COUNT] = {
|
||||
200,
|
||||
220,
|
||||
240,
|
||||
250,
|
||||
260,
|
||||
280,
|
||||
300,
|
||||
325,
|
||||
350,
|
||||
375,
|
||||
400,
|
||||
450,
|
||||
500,
|
||||
600,
|
||||
700,
|
||||
};
|
||||
|
||||
#define INTERPACKET_COUNT 13
|
||||
const char* const interpacket_text[INTERPACKET_COUNT] = {
|
||||
"0us",
|
||||
"2us",
|
||||
"4us",
|
||||
"6us",
|
||||
"8us",
|
||||
"10us",
|
||||
"12us",
|
||||
"14us",
|
||||
"16us",
|
||||
"18us",
|
||||
"20us",
|
||||
"25us",
|
||||
"30us",
|
||||
};
|
||||
const uint32_t interpacket_value[INTERPACKET_COUNT] = {
|
||||
0,
|
||||
2,
|
||||
4,
|
||||
6,
|
||||
8,
|
||||
10,
|
||||
12,
|
||||
14,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
25,
|
||||
30,
|
||||
};
|
||||
|
||||
static void mag_scene_emulate_config_set_tx(VariableItem* item) {
|
||||
Mag* mag = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, tx_text[index]);
|
||||
|
||||
mag->setting->tx = tx_value[index];
|
||||
};
|
||||
|
||||
static void mag_scene_emulate_config_set_track(VariableItem* item) {
|
||||
Mag* mag = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
if(mag->setting->reverse == MagReverseStateOff) {
|
||||
variable_item_set_current_value_text(item, track_text[index]);
|
||||
mag->setting->track = track_value[index];
|
||||
} else if(mag->setting->reverse == MagReverseStateOn) {
|
||||
variable_item_set_current_value_index(
|
||||
item, value_index_uint32(MagTrackStateOneAndTwo, track_value, TRACK_COUNT));
|
||||
}
|
||||
|
||||
// TODO: Check there is data in selected track?
|
||||
// Only display track options with data?
|
||||
};
|
||||
|
||||
static void mag_scene_emulate_config_set_reverse(VariableItem* item) {
|
||||
Mag* mag = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
if(mag->setting->track == MagTrackStateOneAndTwo) {
|
||||
// only allow reverse track to be set when playing both 1 and 2
|
||||
variable_item_set_current_value_text(item, reverse_text[index]);
|
||||
mag->setting->reverse = reverse_value[index];
|
||||
//FURI_LOG_D(TAG, "%s", reverse_text[index]);
|
||||
//FURI_LOG_D(TAG, "%d", mag->setting->reverse);
|
||||
} else {
|
||||
variable_item_set_current_value_index(
|
||||
item, value_index_uint32(MagReverseStateOff, reverse_value, REVERSE_COUNT));
|
||||
}
|
||||
};
|
||||
|
||||
static void mag_scene_emulate_config_set_clock(VariableItem* item) {
|
||||
Mag* mag = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, clock_text[index]);
|
||||
|
||||
mag->setting->us_clock = clock_value[index];
|
||||
};
|
||||
|
||||
static void mag_scene_emulate_config_set_interpacket(VariableItem* item) {
|
||||
Mag* mag = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, interpacket_text[index]);
|
||||
|
||||
mag->setting->us_interpacket = interpacket_value[index];
|
||||
};
|
||||
|
||||
void mag_scene_emulate_config_on_enter(void* context) {
|
||||
// TODO: retrieve current values from struct, rather than setting to default on setup
|
||||
|
||||
Mag* mag = context;
|
||||
VariableItem* item;
|
||||
uint8_t value_index;
|
||||
|
||||
// TX
|
||||
item = variable_item_list_add(
|
||||
mag->variable_item_list, "TX via:", TX_COUNT, mag_scene_emulate_config_set_tx, mag);
|
||||
value_index = value_index_uint32(mag->setting->tx, tx_value, TX_COUNT);
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, tx_text[value_index]);
|
||||
|
||||
// Track
|
||||
item = variable_item_list_add(
|
||||
mag->variable_item_list, "Track:", TRACK_COUNT, mag_scene_emulate_config_set_track, mag);
|
||||
value_index = value_index_uint32(mag->setting->track, track_value, TRACK_COUNT);
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, track_text[value_index]);
|
||||
|
||||
// Reverse
|
||||
//FURI_LOG_D(TAG, "%d", mag->setting->reverse);
|
||||
item = variable_item_list_add(
|
||||
mag->variable_item_list,
|
||||
"Reverse:",
|
||||
REVERSE_COUNT,
|
||||
mag_scene_emulate_config_set_reverse,
|
||||
mag);
|
||||
value_index = value_index_uint32(mag->setting->reverse, reverse_value, REVERSE_COUNT);
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, reverse_text[value_index]);
|
||||
|
||||
// Clock
|
||||
item = variable_item_list_add(
|
||||
mag->variable_item_list, "Clock:", CLOCK_COUNT, mag_scene_emulate_config_set_clock, mag);
|
||||
value_index = value_index_uint32(mag->setting->us_clock, clock_value, CLOCK_COUNT);
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, clock_text[value_index]);
|
||||
|
||||
// Interpacket
|
||||
/*
|
||||
item = variable_item_list_add(
|
||||
mag->variable_item_list,
|
||||
"Interpacket:",
|
||||
INTERPACKET_COUNT,
|
||||
mag_scene_emulate_config_set_interpacket,
|
||||
mag);
|
||||
value_index =
|
||||
value_index_uint32(mag->setting->us_interpacket, interpacket_value, INTERPACKET_COUNT);
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, interpacket_text[value_index]);*/
|
||||
UNUSED(mag_scene_emulate_config_set_interpacket);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewVariableItemList);
|
||||
}
|
||||
|
||||
bool mag_scene_emulate_config_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
UNUSED(mag);
|
||||
UNUSED(scene_manager);
|
||||
UNUSED(event);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_emulate_config_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
variable_item_list_set_selected_item(mag->variable_item_list, 0);
|
||||
variable_item_list_reset(mag->variable_item_list);
|
||||
// mag_last_settings_save?
|
||||
// scene_manager_set_scene_state? Using subghz_scene_reciever_config as framework/inspo
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_exit_confirm_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
UNUSED(mag);
|
||||
}
|
||||
|
||||
bool mag_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
UNUSED(mag);
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_exit_confirm_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
UNUSED(mag);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
#include "../mag_device.h"
|
||||
|
||||
void mag_scene_file_select_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
//UNUSED(mag);
|
||||
mag_device_set_loading_callback(mag->mag_dev, mag_show_loading_popup, mag);
|
||||
if(mag_file_select(mag->mag_dev)) {
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneSavedMenu);
|
||||
} else {
|
||||
scene_manager_search_and_switch_to_previous_scene(mag->scene_manager, MagSceneStart);
|
||||
}
|
||||
mag_device_set_loading_callback(mag->mag_dev, NULL, mag);
|
||||
}
|
||||
|
||||
bool mag_scene_file_select_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void mag_scene_file_select_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
#include <toolbox/name_generator.h>
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_input_name_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
TextInput* text_input = mag->text_input;
|
||||
FuriString* folder_path;
|
||||
folder_path = furi_string_alloc();
|
||||
|
||||
//TODO: compatible types / etc
|
||||
//bool name_is_empty = furi_string_empty(mag->mag_dev->dev_name);
|
||||
bool name_is_empty = true;
|
||||
|
||||
if(name_is_empty) {
|
||||
furi_string_set(mag->file_path, MAG_APP_FOLDER);
|
||||
name_generator_make_auto(mag->text_store, MAG_TEXT_STORE_SIZE, "Mag");
|
||||
furi_string_set(folder_path, MAG_APP_FOLDER);
|
||||
} else {
|
||||
// TODO: compatible types etc
|
||||
//mag_text_store_set(mag, "%s", furi_string_get_cstr(mag->mag_dev->dev_name));
|
||||
path_extract_dirname(furi_string_get_cstr(mag->file_path), folder_path);
|
||||
}
|
||||
|
||||
text_input_set_header_text(text_input, "Name the card");
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
mag_text_input_callback,
|
||||
mag,
|
||||
mag->text_store,
|
||||
MAG_DEV_NAME_MAX_LEN,
|
||||
name_is_empty);
|
||||
|
||||
FURI_LOG_I("", "%s %s", furi_string_get_cstr(folder_path), mag->text_store);
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
furi_string_get_cstr(folder_path),
|
||||
MAG_APP_EXTENSION,
|
||||
furi_string_get_cstr(mag->file_name));
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
|
||||
furi_string_free(folder_path);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewTextInput);
|
||||
}
|
||||
|
||||
bool mag_scene_input_name_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == MagEventNext) {
|
||||
consumed = true;
|
||||
//if(!furi_string_empty(mag->file_name)) {
|
||||
// mag_delete_key(mag);
|
||||
//}
|
||||
|
||||
furi_string_set(mag->file_name, mag->text_store);
|
||||
|
||||
if(mag_device_save(mag->mag_dev, furi_string_get_cstr(mag->file_name))) {
|
||||
scene_manager_next_scene(scene_manager, MagSceneSaveSuccess);
|
||||
} else {
|
||||
//scene_manager_search_and_switch_to_previous_scene(
|
||||
// scene_manager, MagSceneReadKeyMenu);
|
||||
// TODO: Replace with appropriate scene! No read scene prior if adding manually...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_input_name_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
TextInput* text_input = mag->text_input;
|
||||
|
||||
void* validator_context = text_input_get_validator_callback_context(text_input);
|
||||
text_input_set_validator(text_input, NULL, NULL);
|
||||
validator_is_file_free((ValidatorIsFile*)validator_context);
|
||||
|
||||
text_input_reset(text_input);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_input_value_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
TextInput* text_input = mag->text_input;
|
||||
|
||||
// TODO: retrieve stored/existing data if editing rather than adding anew?
|
||||
mag_text_store_set(mag, furi_string_get_cstr(mag->mag_dev->dev_data.track[1].str));
|
||||
|
||||
text_input_set_header_text(text_input, "Enter track data (WIP)");
|
||||
text_input_set_result_callback(
|
||||
text_input, mag_text_input_callback, mag, mag->text_store, MAG_TEXT_STORE_SIZE, true);
|
||||
|
||||
text_input_add_illegal_symbols(text_input);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewTextInput);
|
||||
}
|
||||
|
||||
bool mag_scene_input_value_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == MagEventNext) {
|
||||
consumed = true;
|
||||
|
||||
furi_string_set(mag->mag_dev->dev_data.track[1].str, mag->text_store);
|
||||
scene_manager_next_scene(scene_manager, MagSceneInputName);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_input_value_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
UNUSED(mag);
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
// Creator: Hummus@FlipperGang
|
||||
|
||||
#include "../mag_i.h"
|
||||
#include "../helpers/mag_helpers.h"
|
||||
|
||||
#include "mag_scene_read.h"
|
||||
|
||||
#define TAG "MagSceneRead"
|
||||
|
||||
void uart_callback(UartIrqEvent event, uint8_t data, void* context) {
|
||||
Mag* mag = context;
|
||||
if(event == UartIrqEventRXNE) {
|
||||
furi_stream_buffer_send(mag->uart_rx_stream, &data, 1, 0);
|
||||
furi_thread_flags_set(furi_thread_get_id(mag->uart_rx_thread), WorkerEvtRxDone);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t uart_worker(void* context) {
|
||||
Mag* mag = context;
|
||||
mag->uart_rx_stream = furi_stream_buffer_alloc(UART_RX_BUF_SIZE, 1);
|
||||
mag->uart_text_box_store_strlen = 0;
|
||||
|
||||
while(1) {
|
||||
uint32_t events =
|
||||
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||
// furi_check((events & FuriFlagError) == 0);
|
||||
|
||||
if(events & WorkerEvtStop) break;
|
||||
if(events & WorkerEvtRxDone) {
|
||||
FURI_LOG_D(TAG, "WorkerEvtRxDone");
|
||||
// notification_message(mag->notifications, &sequence_success);
|
||||
size_t len = furi_stream_buffer_receive(
|
||||
mag->uart_rx_stream, mag->uart_rx_buf, UART_RX_BUF_SIZE, 200);
|
||||
FURI_LOG_D(TAG, "UART RX len: %d", len);
|
||||
|
||||
if(len > 0) {
|
||||
// If text box store gets too big, then truncate it
|
||||
mag->uart_text_box_store_strlen += len;
|
||||
|
||||
if(mag->uart_text_box_store_strlen >= UART_TERMINAL_TEXT_BOX_STORE_SIZE - 1) {
|
||||
furi_string_right(
|
||||
mag->uart_text_box_store, mag->uart_text_box_store_strlen / 2);
|
||||
mag->uart_text_box_store_strlen =
|
||||
furi_string_size(mag->uart_text_box_store) + len;
|
||||
}
|
||||
|
||||
// Add '\0' to the end of the string, and then add the new data
|
||||
mag->uart_rx_buf[len] = '\0';
|
||||
furi_string_cat_printf(mag->uart_text_box_store, "%s", mag->uart_rx_buf);
|
||||
|
||||
FURI_LOG_D(TAG, "UART RX buf: %*.s", len, mag->uart_rx_buf);
|
||||
FURI_LOG_D(
|
||||
TAG, "UART RX store: %s", furi_string_get_cstr(mag->uart_text_box_store));
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "UARTEventRxData");
|
||||
|
||||
view_dispatcher_send_custom_event(mag->view_dispatcher, UARTEventRxData);
|
||||
}
|
||||
}
|
||||
|
||||
furi_stream_buffer_free(mag->uart_rx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void update_widgets(Mag* mag) {
|
||||
// Clear widget from all elements
|
||||
widget_reset(mag->widget);
|
||||
|
||||
// Titlebar
|
||||
widget_add_icon_element(mag->widget, 38, -1, &I_mag_file_10px);
|
||||
widget_add_string_element(mag->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "READ");
|
||||
widget_add_icon_element(mag->widget, 81, -1, &I_mag_file_10px);
|
||||
|
||||
// Text box
|
||||
widget_add_text_scroll_element(
|
||||
mag->widget, 0, 10, 128, 40, furi_string_get_cstr(mag->uart_text_box_store));
|
||||
|
||||
// Buttons
|
||||
widget_add_button_element(mag->widget, GuiButtonTypeLeft, "Clear", mag_widget_callback, mag);
|
||||
widget_add_button_element(mag->widget, GuiButtonTypeRight, "Parse", mag_widget_callback, mag);
|
||||
}
|
||||
|
||||
void mag_scene_read_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
FuriString* message = furi_string_alloc();
|
||||
furi_string_printf(message, "Please swipe a card!\n");
|
||||
mag->uart_text_box_store = message;
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
||||
|
||||
update_widgets(mag);
|
||||
|
||||
// Initialize UART
|
||||
// furi_hal_console_disable();
|
||||
furi_hal_uart_deinit(FuriHalUartIdUSART1);
|
||||
furi_hal_uart_init(FuriHalUartIdUSART1, 9600);
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_callback, mag);
|
||||
FURI_LOG_D(TAG, "UART initialized");
|
||||
|
||||
mag->uart_rx_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(mag->uart_rx_thread, "UartRx");
|
||||
furi_thread_set_stack_size(mag->uart_rx_thread, 1024);
|
||||
furi_thread_set_context(mag->uart_rx_thread, mag);
|
||||
furi_thread_set_callback(mag->uart_rx_thread, uart_worker);
|
||||
|
||||
furi_thread_start(mag->uart_rx_thread);
|
||||
FURI_LOG_D(TAG, "UART worker started");
|
||||
}
|
||||
|
||||
bool mag_scene_read_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
FURI_LOG_D(TAG, "Custom event: %ld", event.event);
|
||||
|
||||
switch(event.event) {
|
||||
case GuiButtonTypeLeft: // Clear
|
||||
consumed = true;
|
||||
// Clear text box store
|
||||
furi_string_reset(mag->uart_text_box_store);
|
||||
mag->uart_text_box_store_strlen = 0;
|
||||
break;
|
||||
|
||||
case GuiButtonTypeRight: // Parse
|
||||
consumed = true;
|
||||
FURI_LOG_D(TAG, "Trying to parse");
|
||||
MagDevice* mag_dev = mag->mag_dev;
|
||||
|
||||
bool res = mag_device_parse_card_string(mag_dev, mag->uart_text_box_store);
|
||||
furi_string_reset(mag->uart_text_box_store);
|
||||
if(res) {
|
||||
notification_message(mag->notifications, &sequence_success);
|
||||
|
||||
furi_string_printf(
|
||||
mag->uart_text_box_store,
|
||||
"Track 1: %.*s\nTrack 2: %.*s\nTrack 3: %.*s",
|
||||
mag_dev->dev_data.track[0].len,
|
||||
furi_string_get_cstr(mag_dev->dev_data.track[0].str),
|
||||
mag_dev->dev_data.track[1].len,
|
||||
furi_string_get_cstr(mag_dev->dev_data.track[1].str),
|
||||
mag_dev->dev_data.track[2].len,
|
||||
furi_string_get_cstr(mag_dev->dev_data.track[2].str));
|
||||
|
||||
// Switch to saved menu scene
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneSavedMenu);
|
||||
|
||||
} else {
|
||||
furi_string_printf(mag->uart_text_box_store, "Failed to parse! Try again\n");
|
||||
notification_message(mag->notifications, &sequence_error);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
update_widgets(mag);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_read_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
// notification_message(mag->notifications, &sequence_blink_stop);
|
||||
widget_reset(mag->widget);
|
||||
// view_dispatcher_remove_view(mag->view_dispatcher, MagViewWidget);
|
||||
|
||||
// Stop UART worker
|
||||
FURI_LOG_D(TAG, "Stopping UART worker");
|
||||
furi_thread_flags_set(furi_thread_get_id(mag->uart_rx_thread), WorkerEvtStop);
|
||||
furi_thread_join(mag->uart_rx_thread);
|
||||
furi_thread_free(mag->uart_rx_thread);
|
||||
FURI_LOG_D(TAG, "UART worker stopped");
|
||||
|
||||
furi_string_free(mag->uart_text_box_store);
|
||||
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
|
||||
furi_hal_uart_deinit(FuriHalUartIdUSART1);
|
||||
// furi_hal_console_enable();
|
||||
|
||||
notification_message(mag->notifications, &sequence_blink_stop);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/modules/text_box.h>
|
||||
|
||||
#define UART_RX_BUF_SIZE (320)
|
||||
#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096)
|
||||
#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512)
|
||||
#define UART_CH (FuriHalUartIdUSART1)
|
||||
#define UART_BAUDRATE (9600)
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtStop = (1 << 0),
|
||||
WorkerEvtRxDone = (1 << 1),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
typedef enum {
|
||||
UARTEventRxData = 100,
|
||||
} UARTEvents;
|
||||
|
||||
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
|
||||
@@ -1,43 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_save_success_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Popup* popup = mag->popup;
|
||||
|
||||
// Clear state of data enter scene
|
||||
//scene_manager_set_scene_state(mag->scene_manager, LfRfidSceneSaveData, 0);
|
||||
mag_text_store_clear(mag);
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
popup_set_context(popup, mag);
|
||||
popup_set_callback(popup, mag_popup_timeout_callback);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_enable_timeout(popup);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewPopup);
|
||||
}
|
||||
|
||||
bool mag_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
bool consumed = false;
|
||||
|
||||
if((event.type == SceneManagerEventTypeBack) ||
|
||||
((event.type == SceneManagerEventTypeCustom) && (event.event == MagEventPopupClosed))) {
|
||||
bool result =
|
||||
scene_manager_search_and_switch_to_previous_scene(mag->scene_manager, MagSceneStart);
|
||||
if(!result) {
|
||||
scene_manager_search_and_switch_to_another_scene(
|
||||
mag->scene_manager, MagSceneFileSelect);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_save_success_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
|
||||
popup_reset(mag->popup);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_saved_info_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Widget* widget = mag->widget;
|
||||
|
||||
FuriString* tmp_str;
|
||||
tmp_str = furi_string_alloc();
|
||||
|
||||
// Use strlcpy instead perhaps, to truncate to screen width, then add ellipses if needed?
|
||||
furi_string_printf(tmp_str, "%s\r\n", mag->mag_dev->dev_name);
|
||||
|
||||
widget_add_icon_element(widget, 1, 1, &I_mag_file_10px);
|
||||
widget_add_string_element(
|
||||
widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str));
|
||||
furi_string_reset(tmp_str);
|
||||
|
||||
for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) {
|
||||
FuriString* trackstr = mag->mag_dev->dev_data.track[i].str;
|
||||
|
||||
furi_string_cat_printf(
|
||||
tmp_str,
|
||||
"Track %d:%s%s%s",
|
||||
(i + 1),
|
||||
furi_string_empty(trackstr) ? " " : "\n",
|
||||
furi_string_empty(trackstr) ? "< empty >" : furi_string_get_cstr(trackstr),
|
||||
(i + 1 == MAG_DEV_TRACKS) ? "" : "\n\n");
|
||||
}
|
||||
|
||||
widget_add_text_scroll_element(widget, 0, 15, 128, 49, furi_string_get_cstr(tmp_str));
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
||||
furi_string_free(tmp_str);
|
||||
}
|
||||
|
||||
bool mag_scene_saved_info_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
UNUSED(event);
|
||||
UNUSED(scene_manager);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_saved_info_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
widget_reset(mag->widget);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexEmulate,
|
||||
//SubmenuIndexEdit,
|
||||
SubmenuIndexDelete,
|
||||
SubmenuIndexInfo,
|
||||
};
|
||||
|
||||
void mag_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
|
||||
Mag* mag = context;
|
||||
|
||||
view_dispatcher_send_custom_event(mag->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void mag_scene_saved_menu_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Submenu* submenu = mag->submenu;
|
||||
|
||||
// messy code to quickly check which tracks are available for emulation/display
|
||||
// there's likely a better spot to do this, but the MagDevice functions don't have access to the full mag struct...
|
||||
bool is_empty_t1 = furi_string_empty(mag->mag_dev->dev_data.track[0].str);
|
||||
bool is_empty_t2 = furi_string_empty(mag->mag_dev->dev_data.track[1].str);
|
||||
bool is_empty_t3 = furi_string_empty(mag->mag_dev->dev_data.track[2].str);
|
||||
|
||||
if(!is_empty_t1 && !is_empty_t2) {
|
||||
mag->setting->track = MagTrackStateOneAndTwo;
|
||||
} else if(!is_empty_t1) {
|
||||
mag->setting->track = MagTrackStateOne;
|
||||
} else if(!is_empty_t2) {
|
||||
mag->setting->track = MagTrackStateTwo;
|
||||
} else if(!is_empty_t3) {
|
||||
mag->setting->track = MagTrackStateThree;
|
||||
} // TODO: what happens if no track data present?
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Emulate (WIP)", SubmenuIndexEmulate, mag_scene_saved_menu_submenu_callback, mag);
|
||||
//submenu_add_item(
|
||||
// submenu, "Edit (WIP)", SubmenuIndexEdit, mag_scene_saved_menu_submenu_callback, mag);
|
||||
submenu_add_item(
|
||||
submenu, "Delete", SubmenuIndexDelete, mag_scene_saved_menu_submenu_callback, mag);
|
||||
submenu_add_item(
|
||||
submenu, "Info", SubmenuIndexInfo, mag_scene_saved_menu_submenu_callback, mag);
|
||||
|
||||
submenu_set_selected_item(
|
||||
mag->submenu, scene_manager_get_scene_state(mag->scene_manager, MagSceneSavedMenu));
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewSubmenu);
|
||||
}
|
||||
|
||||
bool mag_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneSavedMenu, event.event);
|
||||
|
||||
// TODO: replace with actual next scenes once built
|
||||
if(event.event == SubmenuIndexEmulate) {
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneEmulate);
|
||||
consumed = true;
|
||||
//} else if(event.event == SubmenuIndexEdit) {
|
||||
// scene_manager_next_scene(mag->scene_manager, MagSceneUnderConstruction);
|
||||
// consumed = true;
|
||||
} else if(event.event == SubmenuIndexDelete) {
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneDeleteConfirm);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexInfo) {
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneSavedInfo);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_saved_menu_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
|
||||
submenu_reset(mag->submenu);
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexSaved,
|
||||
SubmenuIndexRead,
|
||||
//SubmenuIndexAddManually,
|
||||
SubmenuIndexAbout,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void mag_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
Mag* mag = context;
|
||||
|
||||
view_dispatcher_send_custom_event(mag->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void mag_scene_start_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Submenu* submenu = mag->submenu;
|
||||
|
||||
submenu_add_item(submenu, "Saved", SubmenuIndexSaved, mag_scene_start_submenu_callback, mag);
|
||||
submenu_add_item(submenu, "Read", SubmenuIndexRead, mag_scene_start_submenu_callback, mag);
|
||||
//submenu_add_item(
|
||||
// submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag);
|
||||
submenu_add_item(submenu, "About", SubmenuIndexAbout, mag_scene_start_submenu_callback, mag);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(mag->scene_manager, MagSceneStart));
|
||||
|
||||
// clear key
|
||||
furi_string_reset(mag->file_name);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewSubmenu);
|
||||
}
|
||||
|
||||
bool mag_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case SubmenuIndexSaved:
|
||||
furi_string_set(mag->file_path, MAG_APP_FOLDER);
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneFileSelect);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case SubmenuIndexRead:
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneRead);
|
||||
consumed = true;
|
||||
break;
|
||||
//case SubmenuIndexAddManually:
|
||||
// scene_manager_next_scene(mag->scene_manager, MagSceneInputValue);
|
||||
// consumed = true;
|
||||
// break;
|
||||
case SubmenuIndexAbout:
|
||||
scene_manager_next_scene(mag->scene_manager, MagSceneAbout);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
scene_manager_set_scene_state(mag->scene_manager, MagSceneStart, event.event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_start_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
|
||||
submenu_reset(mag->submenu);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#include "../mag_i.h"
|
||||
|
||||
void mag_scene_under_construction_on_enter(void* context) {
|
||||
Mag* mag = context;
|
||||
Widget* widget = mag->widget;
|
||||
|
||||
FuriString* tmp_str;
|
||||
tmp_str = furi_string_alloc();
|
||||
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", mag_widget_callback, mag);
|
||||
|
||||
furi_string_printf(tmp_str, "Under construction!");
|
||||
widget_add_string_element(
|
||||
widget, 64, 4, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str));
|
||||
furi_string_reset(tmp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
||||
furi_string_free(tmp_str);
|
||||
}
|
||||
|
||||
bool mag_scene_under_construction_on_event(void* context, SceneManagerEvent event) {
|
||||
Mag* mag = context;
|
||||
SceneManager* scene_manager = mag->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
consumed = true;
|
||||
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mag_scene_under_construction_on_exit(void* context) {
|
||||
Mag* mag = context;
|
||||
widget_reset(mag->widget);
|
||||
}
|
||||
674
applications/external/minesweeper/LICENSE
vendored
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -1,13 +0,0 @@
|
||||
App(
|
||||
appid="minesweeper",
|
||||
name="Minesweeper",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="minesweeper_app",
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
fap_category="Games",
|
||||
fap_icon="minesweeper_icon.png",
|
||||
fap_author="@panki27 & @xMasterX",
|
||||
fap_version="1.0",
|
||||
fap_description="Minesweeper Game",
|
||||
)
|
||||
144
applications/external/minesweeper/assets.h
vendored
@@ -1,144 +0,0 @@
|
||||
#define tile_0_width 8
|
||||
#define tile_0_height 8
|
||||
static uint8_t tile_0_bits[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
#define tile_1_width 8
|
||||
#define tile_1_height 8
|
||||
static uint8_t tile_1_bits[] = {
|
||||
0x00,
|
||||
0x10,
|
||||
0x18,
|
||||
0x10,
|
||||
0x10,
|
||||
0x10,
|
||||
0x10,
|
||||
0x00,
|
||||
};
|
||||
#define tile_2_width 8
|
||||
#define tile_2_height 8
|
||||
static uint8_t tile_2_bits[] = {
|
||||
0x00,
|
||||
0x1C,
|
||||
0x20,
|
||||
0x20,
|
||||
0x18,
|
||||
0x04,
|
||||
0x3C,
|
||||
0x00,
|
||||
};
|
||||
#define tile_3_width 8
|
||||
#define tile_3_height 8
|
||||
static uint8_t tile_3_bits[] = {
|
||||
0x00,
|
||||
0x1C,
|
||||
0x20,
|
||||
0x20,
|
||||
0x18,
|
||||
0x20,
|
||||
0x1C,
|
||||
0x00,
|
||||
};
|
||||
#define tile_4_width 8
|
||||
#define tile_4_height 8
|
||||
static uint8_t tile_4_bits[] = {
|
||||
0x00,
|
||||
0x04,
|
||||
0x14,
|
||||
0x14,
|
||||
0x3C,
|
||||
0x10,
|
||||
0x10,
|
||||
0x00,
|
||||
};
|
||||
#define tile_5_width 8
|
||||
#define tile_5_height 8
|
||||
static uint8_t tile_5_bits[] = {
|
||||
0x00,
|
||||
0x3C,
|
||||
0x04,
|
||||
0x1C,
|
||||
0x20,
|
||||
0x20,
|
||||
0x1C,
|
||||
0x00,
|
||||
};
|
||||
#define tile_6_width 8
|
||||
#define tile_6_height 8
|
||||
static uint8_t tile_6_bits[] = {
|
||||
0x00,
|
||||
0x18,
|
||||
0x24,
|
||||
0x04,
|
||||
0x1C,
|
||||
0x24,
|
||||
0x18,
|
||||
0x00,
|
||||
};
|
||||
#define tile_7_width 8
|
||||
#define tile_7_height 8
|
||||
static uint8_t tile_7_bits[] = {
|
||||
0x00,
|
||||
0x3C,
|
||||
0x20,
|
||||
0x20,
|
||||
0x10,
|
||||
0x08,
|
||||
0x08,
|
||||
0x00,
|
||||
};
|
||||
#define tile_8_width 8
|
||||
#define tile_8_height 8
|
||||
static uint8_t tile_8_bits[] = {
|
||||
0x00,
|
||||
0x18,
|
||||
0x24,
|
||||
0x18,
|
||||
0x24,
|
||||
0x24,
|
||||
0x18,
|
||||
0x00,
|
||||
};
|
||||
#define tile_flag_width 8
|
||||
#define tile_flag_height 8
|
||||
static uint8_t tile_flag_bits[] = {
|
||||
0xFF,
|
||||
0x81,
|
||||
0xB9,
|
||||
0x89,
|
||||
0x89,
|
||||
0x9D,
|
||||
0x81,
|
||||
0xFF,
|
||||
};
|
||||
#define tile_mine_width 8
|
||||
#define tile_mine_height 8
|
||||
static uint8_t tile_mine_bits[] = {
|
||||
0x55,
|
||||
0xAA,
|
||||
0x55,
|
||||
0xAA,
|
||||
0x55,
|
||||
0xAA,
|
||||
0x55,
|
||||
0xAA,
|
||||
};
|
||||
#define tile_uncleared_width 8
|
||||
#define tile_uncleared_height 8
|
||||
static uint8_t tile_uncleared_bits[] = {
|
||||
0xFF,
|
||||
0x81,
|
||||
0x81,
|
||||
0x81,
|
||||
0x81,
|
||||
0x81,
|
||||
0x81,
|
||||
0xFF,
|
||||
};
|
||||
48
applications/external/minesweeper/assets/asset
vendored
@@ -1,48 +0,0 @@
|
||||
#define tile_0_width 8
|
||||
#define tile_0_height 8
|
||||
static uint8_t tile_0_bits[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, };
|
||||
#define tile_1_width 8
|
||||
#define tile_1_height 8
|
||||
static uint8_t tile_1_bits[] = {
|
||||
0x00, 0x10, 0x18, 0x10, 0x10, 0x10, 0x10, 0x00, };
|
||||
#define tile_2_width 8
|
||||
#define tile_2_height 8
|
||||
static uint8_t tile_2_bits[] = {
|
||||
0x00, 0x1C, 0x20, 0x20, 0x18, 0x04, 0x3C, 0x00, };
|
||||
#define tile_3_width 8
|
||||
#define tile_3_height 8
|
||||
static uint8_t tile_3_bits[] = {
|
||||
0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x1C, 0x00, };
|
||||
#define tile_4_width 8
|
||||
#define tile_4_height 8
|
||||
static uint8_t tile_4_bits[] = {
|
||||
0x00, 0x04, 0x14, 0x14, 0x3C, 0x10, 0x10, 0x00, };
|
||||
#define tile_5_width 8
|
||||
#define tile_5_height 8
|
||||
static uint8_t tile_5_bits[] = {
|
||||
0x00, 0x3C, 0x04, 0x1C, 0x20, 0x20, 0x1C, 0x00, };
|
||||
#define tile_6_width 8
|
||||
#define tile_6_height 8
|
||||
static uint8_t tile_6_bits[] = {
|
||||
0x00, 0x18, 0x24, 0x04, 0x1C, 0x24, 0x18, 0x00, };
|
||||
#define tile_7_width 8
|
||||
#define tile_7_height 8
|
||||
static uint8_t tile_7_bits[] = {
|
||||
0x00, 0x3C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x00, };
|
||||
#define tile_8_width 8
|
||||
#define tile_8_height 8
|
||||
static uint8_t tile_8_bits[] = {
|
||||
0x00, 0x18, 0x24, 0x18, 0x24, 0x24, 0x18, 0x00, };
|
||||
#define tile_flag_width 8
|
||||
#define tile_flag_height 8
|
||||
static uint8_t tile_flag_bits[] = {
|
||||
0xFF, 0x81, 0xB9, 0x89, 0x89, 0x9D, 0x81, 0xFF, };
|
||||
#define tile_mine_width 8
|
||||
#define tile_mine_height 8
|
||||
static uint8_t tile_mine_bits[] = {
|
||||
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, };
|
||||
#define tile_uncleared_width 8
|
||||
#define tile_uncleared_height 8
|
||||
static uint8_t tile_uncleared_bits[] = {
|
||||
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, };
|
||||
BIN
applications/external/minesweeper/assets/mockup.png
vendored
|
Before Width: | Height: | Size: 400 B |
BIN
applications/external/minesweeper/assets/tile_0.png
vendored
|
Before Width: | Height: | Size: 130 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_0_width 8
|
||||
#define tile_0_height 8
|
||||
static char tile_0_bits[] = {
|
||||
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_1.png
vendored
|
Before Width: | Height: | Size: 152 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_1_width 8
|
||||
#define tile_1_height 8
|
||||
static uint8_t tile_1_bits[] = {
|
||||
0x00, 0x10, 0x18, 0x10, 0x10, 0x10, 0x10, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_2.png
vendored
|
Before Width: | Height: | Size: 164 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_2_width 8
|
||||
#define tile_2_height 8
|
||||
static uint8_t tile_2_bits[] = {
|
||||
0x00, 0x1C, 0x20, 0x20, 0x18, 0x04, 0x3C, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_3.png
vendored
|
Before Width: | Height: | Size: 166 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_3_width 8
|
||||
#define tile_3_height 8
|
||||
static uint8_t tile_3_bits[] = {
|
||||
0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x1C, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_4.png
vendored
|
Before Width: | Height: | Size: 164 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_4_width 8
|
||||
#define tile_4_height 8
|
||||
static uint8_t tile_4_bits[] = {
|
||||
0x00, 0x04, 0x14, 0x14, 0x3C, 0x10, 0x10, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_5.png
vendored
|
Before Width: | Height: | Size: 167 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_5_width 8
|
||||
#define tile_5_height 8
|
||||
static uint8_t tile_5_bits[] = {
|
||||
0x00, 0x3C, 0x04, 0x1C, 0x20, 0x20, 0x1C, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_6.png
vendored
|
Before Width: | Height: | Size: 164 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_6_width 8
|
||||
#define tile_6_height 8
|
||||
static uint8_t tile_6_bits[] = {
|
||||
0x00, 0x18, 0x24, 0x04, 0x1C, 0x24, 0x18, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_7.png
vendored
|
Before Width: | Height: | Size: 165 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_7_width 8
|
||||
#define tile_7_height 8
|
||||
static uint8_t tile_7_bits[] = {
|
||||
0x00, 0x3C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x00, };
|
||||
BIN
applications/external/minesweeper/assets/tile_8.png
vendored
|
Before Width: | Height: | Size: 161 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_8_width 8
|
||||
#define tile_8_height 8
|
||||
static uint8_t tile_8_bits[] = {
|
||||
0x00, 0x18, 0x24, 0x18, 0x24, 0x24, 0x18, 0x00, };
|
||||
|
Before Width: | Height: | Size: 131 B |
|
Before Width: | Height: | Size: 170 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_flag_width 8
|
||||
#define tile_flag_height 8
|
||||
static uint8_t tile_flag_bits[] = {
|
||||
0xFF, 0x81, 0xB9, 0x89, 0x89, 0x9D, 0x81, 0xFF, };
|
||||
|
Before Width: | Height: | Size: 152 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_mine_width 8
|
||||
#define tile_mine_height 8
|
||||
static uint8_t tile_mine_bits[] = {
|
||||
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, };
|
||||
|
Before Width: | Height: | Size: 127 B |
@@ -1,4 +0,0 @@
|
||||
#define tile_uncleared_width 8
|
||||
#define tile_uncleared_height 8
|
||||
static uint8_t tile_uncleared_bits[] = {
|
||||
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, };
|
||||
515
applications/external/minesweeper/minesweeper.c
vendored
@@ -1,515 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#include "assets.h"
|
||||
|
||||
#define PLAYFIELD_WIDTH 16
|
||||
#define PLAYFIELD_HEIGHT 7
|
||||
#define TILE_WIDTH 8
|
||||
#define TILE_HEIGHT 8
|
||||
|
||||
#define MINECOUNT 20
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
typedef enum {
|
||||
TileType0, // this HAS to be in order, for hint assignment to be ez pz
|
||||
TileType1,
|
||||
TileType2,
|
||||
TileType3,
|
||||
TileType4,
|
||||
TileType5,
|
||||
TileType6,
|
||||
TileType7,
|
||||
TileType8,
|
||||
TileTypeUncleared,
|
||||
TileTypeFlag,
|
||||
TileTypeMine
|
||||
} TileType;
|
||||
|
||||
typedef enum { FieldEmpty, FieldMine } Field;
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
DialogsApp* dialogs;
|
||||
NotificationApp* notifications;
|
||||
Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
|
||||
TileType playfield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
|
||||
int cursor_x;
|
||||
int cursor_y;
|
||||
int mines_left;
|
||||
int fields_cleared;
|
||||
int flags_set;
|
||||
bool game_started;
|
||||
uint32_t game_started_tick;
|
||||
} Minesweeper;
|
||||
|
||||
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
const Minesweeper* minesweeper_state = ctx;
|
||||
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
|
||||
|
||||
FuriString* mineStr;
|
||||
FuriString* timeStr;
|
||||
mineStr = furi_string_alloc();
|
||||
timeStr = furi_string_alloc();
|
||||
|
||||
furi_string_printf(mineStr, "Mines: %d", MINECOUNT - minesweeper_state->flags_set);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(mineStr));
|
||||
|
||||
int seconds = 0;
|
||||
int minutes = 0;
|
||||
if(minesweeper_state->game_started) {
|
||||
uint32_t ticks_elapsed = furi_get_tick() - minesweeper_state->game_started_tick;
|
||||
seconds = (int)ticks_elapsed / furi_kernel_get_tick_frequency();
|
||||
minutes = (int)seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
}
|
||||
furi_string_printf(timeStr, "%01d:%02d", minutes, seconds);
|
||||
canvas_draw_str_aligned(canvas, 128, 0, AlignRight, AlignTop, furi_string_get_cstr(timeStr));
|
||||
|
||||
uint8_t* tile_to_draw;
|
||||
|
||||
for(int y = 0; y < PLAYFIELD_HEIGHT; y++) {
|
||||
for(int x = 0; x < PLAYFIELD_WIDTH; x++) {
|
||||
if(x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) {
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
switch(minesweeper_state->playfield[x][y]) {
|
||||
case TileType0:
|
||||
tile_to_draw = tile_0_bits;
|
||||
break;
|
||||
case TileType1:
|
||||
tile_to_draw = tile_1_bits;
|
||||
break;
|
||||
case TileType2:
|
||||
tile_to_draw = tile_2_bits;
|
||||
break;
|
||||
case TileType3:
|
||||
tile_to_draw = tile_3_bits;
|
||||
break;
|
||||
case TileType4:
|
||||
tile_to_draw = tile_4_bits;
|
||||
break;
|
||||
case TileType5:
|
||||
tile_to_draw = tile_5_bits;
|
||||
break;
|
||||
case TileType6:
|
||||
tile_to_draw = tile_6_bits;
|
||||
break;
|
||||
case TileType7:
|
||||
tile_to_draw = tile_7_bits;
|
||||
break;
|
||||
case TileType8:
|
||||
tile_to_draw = tile_8_bits;
|
||||
break;
|
||||
case TileTypeFlag:
|
||||
tile_to_draw = tile_flag_bits;
|
||||
break;
|
||||
case TileTypeUncleared:
|
||||
tile_to_draw = tile_uncleared_bits;
|
||||
break;
|
||||
case TileTypeMine:
|
||||
tile_to_draw = tile_mine_bits;
|
||||
break;
|
||||
default:
|
||||
// this should never happen
|
||||
tile_to_draw = tile_mine_bits;
|
||||
break;
|
||||
}
|
||||
canvas_draw_xbm(
|
||||
canvas,
|
||||
x * TILE_HEIGHT, // x
|
||||
8 + (y * TILE_WIDTH), // y
|
||||
TILE_WIDTH,
|
||||
TILE_HEIGHT,
|
||||
tile_to_draw);
|
||||
if(x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) {
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(mineStr);
|
||||
furi_string_free(timeStr);
|
||||
furi_mutex_release(minesweeper_state->mutex);
|
||||
}
|
||||
|
||||
static void setup_playfield(Minesweeper* minesweeper_state) {
|
||||
int mines_left = MINECOUNT;
|
||||
for(int y = 0; y < PLAYFIELD_HEIGHT; y++) {
|
||||
for(int x = 0; x < PLAYFIELD_WIDTH; x++) {
|
||||
minesweeper_state->minefield[x][y] = FieldEmpty;
|
||||
minesweeper_state->playfield[x][y] = TileTypeUncleared;
|
||||
}
|
||||
}
|
||||
while(mines_left > 0) {
|
||||
int rand_x = rand() % PLAYFIELD_WIDTH;
|
||||
int rand_y = rand() % PLAYFIELD_HEIGHT;
|
||||
// make sure first guess isn't a mine
|
||||
if(minesweeper_state->minefield[rand_x][rand_y] == FieldEmpty &&
|
||||
(minesweeper_state->cursor_x != rand_x || minesweeper_state->cursor_y != rand_y)) {
|
||||
minesweeper_state->minefield[rand_x][rand_y] = FieldMine;
|
||||
mines_left--;
|
||||
}
|
||||
}
|
||||
minesweeper_state->mines_left = MINECOUNT;
|
||||
minesweeper_state->fields_cleared = 0;
|
||||
minesweeper_state->flags_set = 0;
|
||||
minesweeper_state->game_started_tick = furi_get_tick();
|
||||
minesweeper_state->game_started = false;
|
||||
}
|
||||
|
||||
static void place_flag(Minesweeper* minesweeper_state) {
|
||||
if(minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] ==
|
||||
TileTypeUncleared) {
|
||||
minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] =
|
||||
TileTypeFlag;
|
||||
minesweeper_state->flags_set++;
|
||||
} else if(
|
||||
minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] ==
|
||||
TileTypeFlag) {
|
||||
minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] =
|
||||
TileTypeUncleared;
|
||||
minesweeper_state->flags_set--;
|
||||
}
|
||||
}
|
||||
|
||||
static bool game_lost(Minesweeper* minesweeper_state) {
|
||||
// returns true if the player wants to restart, otherwise false
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
dialog_message_set_header(message, "Game Over", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_message_set_text(message, "You hit a mine!", 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_buttons(message, NULL, "Play again", NULL);
|
||||
|
||||
// Set cursor to initial position
|
||||
minesweeper_state->cursor_x = 0;
|
||||
minesweeper_state->cursor_y = 0;
|
||||
|
||||
notification_message(minesweeper_state->notifications, &sequence_single_vibro);
|
||||
|
||||
DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message);
|
||||
dialog_message_free(message);
|
||||
|
||||
return choice == DialogMessageButtonCenter;
|
||||
}
|
||||
|
||||
static bool game_won(Minesweeper* minesweeper_state) {
|
||||
FuriString* tempStr;
|
||||
tempStr = furi_string_alloc();
|
||||
|
||||
int seconds = 0;
|
||||
int minutes = 0;
|
||||
uint32_t ticks_elapsed = furi_get_tick() - minesweeper_state->game_started_tick;
|
||||
seconds = (int)ticks_elapsed / furi_kernel_get_tick_frequency();
|
||||
minutes = (int)seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
const char* header_text = "Game won!";
|
||||
furi_string_cat_printf(tempStr, "Minefield cleared in %01d:%02d", minutes, seconds);
|
||||
dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
|
||||
dialog_message_set_text(
|
||||
message, furi_string_get_cstr(tempStr), 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_buttons(message, NULL, "Play again", NULL);
|
||||
|
||||
// Call dolphin deed when we win the game
|
||||
dolphin_deed(DolphinDeedPluginGameWin);
|
||||
|
||||
DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message);
|
||||
dialog_message_free(message);
|
||||
furi_string_free(tempStr);
|
||||
return choice == DialogMessageButtonCenter;
|
||||
}
|
||||
|
||||
// returns false if the move loses the game - otherwise true
|
||||
static bool play_move(Minesweeper* minesweeper_state, int cursor_x, int cursor_y) {
|
||||
if(minesweeper_state->playfield[cursor_x][cursor_y] == TileTypeFlag) {
|
||||
// we're on a flagged field, do nothing
|
||||
return true;
|
||||
}
|
||||
if(minesweeper_state->minefield[cursor_x][cursor_y] == FieldMine) {
|
||||
// player loses - draw mine
|
||||
minesweeper_state->playfield[cursor_x][cursor_y] = TileTypeMine;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(minesweeper_state->playfield[cursor_x][cursor_y] >= TileType1 &&
|
||||
minesweeper_state->playfield[cursor_x][cursor_y] <= TileType8) {
|
||||
// click on a cleared cell with a number
|
||||
// count the flags around
|
||||
int flags = 0;
|
||||
for(int y = cursor_y - 1; y <= cursor_y + 1; y++) {
|
||||
for(int x = cursor_x - 1; x <= cursor_x + 1; x++) {
|
||||
if(x == cursor_x && y == cursor_y) {
|
||||
// we're on the cell the user selected, so ignore.
|
||||
continue;
|
||||
}
|
||||
// make sure we don't go OOB
|
||||
if(x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT) {
|
||||
if(minesweeper_state->playfield[x][y] == TileTypeFlag) {
|
||||
flags++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int mines = minesweeper_state->playfield[cursor_x][cursor_y]; // ¯\_(ツ)_/¯
|
||||
if(flags == mines) {
|
||||
// auto uncover all non-flags around (to win faster ;)
|
||||
for(int auto_y = cursor_y - 1; auto_y <= cursor_y + 1; auto_y++) {
|
||||
for(int auto_x = cursor_x - 1; auto_x <= cursor_x + 1; auto_x++) {
|
||||
if(auto_x == cursor_x && auto_y == cursor_y) {
|
||||
continue;
|
||||
}
|
||||
if(auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 &&
|
||||
auto_y < PLAYFIELD_HEIGHT) {
|
||||
if(minesweeper_state->playfield[auto_x][auto_y] == TileTypeUncleared) {
|
||||
if(!play_move(minesweeper_state, auto_x, auto_y)) {
|
||||
// flags were wrong, we got a mine!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we're done without hitting a mine - so return
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate number of surrounding mines.
|
||||
int hint = 0;
|
||||
for(int y = cursor_y - 1; y <= cursor_y + 1; y++) {
|
||||
for(int x = cursor_x - 1; x <= cursor_x + 1; x++) {
|
||||
if(x == cursor_x && y == cursor_y) {
|
||||
// we're on the cell the user selected, so ignore.
|
||||
continue;
|
||||
}
|
||||
// make sure we don't go OOB
|
||||
if(x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT) {
|
||||
if(minesweeper_state->minefield[x][y] == FieldMine) {
|
||||
hint++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 〜( ̄▽ ̄〜) don't judge me (〜 ̄▽ ̄)〜
|
||||
minesweeper_state->playfield[cursor_x][cursor_y] = hint;
|
||||
minesweeper_state->fields_cleared++;
|
||||
FURI_LOG_D("Minesweeper", "Setting %d,%d to %d", cursor_x, cursor_y, hint);
|
||||
if(hint == 0) {
|
||||
// the field is "empty"
|
||||
// auto open surrounding fields.
|
||||
for(int auto_y = cursor_y - 1; auto_y <= cursor_y + 1; auto_y++) {
|
||||
for(int auto_x = cursor_x - 1; auto_x <= cursor_x + 1; auto_x++) {
|
||||
if(auto_x == cursor_x && auto_y == cursor_y) {
|
||||
continue;
|
||||
}
|
||||
if(auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 &&
|
||||
auto_y < PLAYFIELD_HEIGHT) {
|
||||
if(minesweeper_state->playfield[auto_x][auto_y] == TileTypeUncleared) {
|
||||
play_move(minesweeper_state, auto_x, auto_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void minesweeper_state_init(Minesweeper* const minesweeper_state) {
|
||||
minesweeper_state->cursor_x = minesweeper_state->cursor_y = 0;
|
||||
minesweeper_state->game_started = false;
|
||||
for(int y = 0; y < PLAYFIELD_HEIGHT; y++) {
|
||||
for(int x = 0; x < PLAYFIELD_WIDTH; x++) {
|
||||
minesweeper_state->playfield[x][y] = TileTypeUncleared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t minesweeper_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
|
||||
|
||||
Minesweeper* minesweeper_state = malloc(sizeof(Minesweeper));
|
||||
// setup
|
||||
minesweeper_state_init(minesweeper_state);
|
||||
|
||||
minesweeper_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
if(!minesweeper_state->mutex) {
|
||||
FURI_LOG_E("Minesweeper", "cannot create mutex\r\n");
|
||||
free(minesweeper_state);
|
||||
return 255;
|
||||
}
|
||||
// BEGIN IMPLEMENTATION
|
||||
|
||||
minesweeper_state->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
minesweeper_state->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
dialog_message_set_header(message, "Minesweeper", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_message_set_text(
|
||||
message,
|
||||
"Hold OK pressed to toggle flags.\ngithub.com/panki27",
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
dialog_message_set_buttons(message, NULL, "Play", NULL);
|
||||
|
||||
dialog_message_show(minesweeper_state->dialogs, message);
|
||||
dialog_message_free(message);
|
||||
|
||||
// Set system callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, render_callback, minesweeper_state);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Call dolphin deed on game start
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
PluginEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypeShort) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
|
||||
minesweeper_state->cursor_y--;
|
||||
if(minesweeper_state->cursor_y < 0) {
|
||||
minesweeper_state->cursor_y = PLAYFIELD_HEIGHT - 1;
|
||||
}
|
||||
furi_mutex_release(minesweeper_state->mutex);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
|
||||
minesweeper_state->cursor_y++;
|
||||
if(minesweeper_state->cursor_y >= PLAYFIELD_HEIGHT) {
|
||||
minesweeper_state->cursor_y = 0;
|
||||
}
|
||||
furi_mutex_release(minesweeper_state->mutex);
|
||||
break;
|
||||
case InputKeyRight:
|
||||
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
|
||||
minesweeper_state->cursor_x++;
|
||||
if(minesweeper_state->cursor_x >= PLAYFIELD_WIDTH) {
|
||||
minesweeper_state->cursor_x = 0;
|
||||
}
|
||||
furi_mutex_release(minesweeper_state->mutex);
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
|
||||
minesweeper_state->cursor_x--;
|
||||
if(minesweeper_state->cursor_x < 0) {
|
||||
minesweeper_state->cursor_x = PLAYFIELD_WIDTH - 1;
|
||||
}
|
||||
furi_mutex_release(minesweeper_state->mutex);
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if(!minesweeper_state->game_started) {
|
||||
setup_playfield(minesweeper_state);
|
||||
minesweeper_state->game_started = true;
|
||||
}
|
||||
if(!play_move(
|
||||
minesweeper_state,
|
||||
minesweeper_state->cursor_x,
|
||||
minesweeper_state->cursor_y)) {
|
||||
// ooops. looks like we hit a mine!
|
||||
if(game_lost(minesweeper_state)) {
|
||||
// player wants to restart.
|
||||
setup_playfield(minesweeper_state);
|
||||
} else {
|
||||
// player wants to exit :(
|
||||
processing = false;
|
||||
}
|
||||
} else {
|
||||
// check win condition.
|
||||
if(minesweeper_state->fields_cleared ==
|
||||
(PLAYFIELD_HEIGHT * PLAYFIELD_WIDTH) - MINECOUNT) {
|
||||
if(game_won(minesweeper_state)) {
|
||||
//player wants to restart
|
||||
setup_playfield(minesweeper_state);
|
||||
} else {
|
||||
processing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
// Exit the plugin
|
||||
processing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event.input.type == InputTypeLong) {
|
||||
// hold events
|
||||
FURI_LOG_D("Minesweeper", "Got a long press!");
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
case InputKeyRight:
|
||||
case InputKeyLeft:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
FURI_LOG_D("Minesweeper", "Toggling flag");
|
||||
furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
|
||||
place_flag(minesweeper_state);
|
||||
furi_mutex_release(minesweeper_state->mutex);
|
||||
break;
|
||||
case InputKeyBack:
|
||||
processing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_mutex_free(minesweeper_state->mutex);
|
||||
free(minesweeper_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 161 B |
21
applications/external/wiiec/LICENSE
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 BlueChip
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
applications/external/wiiec/WiiEC.png
vendored
|
Before Width: | Height: | Size: 116 B |
21
applications/external/wiiec/_image_tool/LICENSE
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 BlueChip
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
148
applications/external/wiiec/_image_tool/_convert.c
vendored
@@ -1,148 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const unsigned char* pp = NULL;
|
||||
uint32_t pix = 0;
|
||||
int bit = 0;
|
||||
|
||||
uint8_t b = 0;
|
||||
uint8_t bcnt = 0;
|
||||
|
||||
unsigned int lcnt = 0;
|
||||
static const int lmax = 16; // max hex values per line
|
||||
|
||||
uint8_t* buf = NULL;
|
||||
uint8_t* bp = NULL;
|
||||
unsigned int blen = 0;
|
||||
|
||||
uint8_t* cmp = NULL;
|
||||
uint8_t* cp = NULL;
|
||||
unsigned int clen = 0;
|
||||
uint8_t ctag = 0xFF;
|
||||
uint32_t tag[256] = {0};
|
||||
uint32_t tmax = UINT32_MAX;
|
||||
|
||||
unsigned int x, y, z;
|
||||
|
||||
const char* name = argv[1];
|
||||
FILE* fh = fopen(argv[2], "wb");
|
||||
|
||||
uint32_t white = 0xFF;
|
||||
|
||||
int rv = 0; // assume success
|
||||
|
||||
// allocate buffers
|
||||
blen = ((img.w * img.h) + 0x7) >> 3;
|
||||
bp = (buf = calloc(blen + 1, 1));
|
||||
cp = (cmp = calloc(blen + 4, 1));
|
||||
|
||||
// sanity check
|
||||
if(!fh || !buf || !cmp) {
|
||||
printf("! fopen() or malloc() fail.\n");
|
||||
rv = 255;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Find white value
|
||||
for(x = 1; x < img.bpp; x++) white = (white << 8) | 0xFF;
|
||||
|
||||
// build bit pattern
|
||||
// create the comment as we go
|
||||
for(pp = img.b, y = 0; y < img.h; y++) {
|
||||
fprintf(fh, "// ");
|
||||
for(x = 0; x < img.w; x++) {
|
||||
// read pixel
|
||||
for(pix = 0, z = 0; z < img.bpp; pix = (pix << 8) | *pp++, z++)
|
||||
;
|
||||
// get bit and draw
|
||||
if(pix < white) {
|
||||
b = (b << 1) | 1;
|
||||
fprintf(fh, "##");
|
||||
} else {
|
||||
b <<= 1;
|
||||
fprintf(fh, "..");
|
||||
}
|
||||
// got byte
|
||||
if((++bcnt) == 8) {
|
||||
*bp++ = b;
|
||||
tag[b]++;
|
||||
bcnt = (b = 0);
|
||||
}
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
// padding
|
||||
if(bcnt) {
|
||||
b <<= (bcnt = 8 - bcnt);
|
||||
*bp++ = b;
|
||||
tag[b]++;
|
||||
}
|
||||
// Kill the compression
|
||||
*bp = ~bp[-1]; // https://youtube.com/clip/Ugkx-JZIr16hETy7hz_H6yIdKPtxVe8C5w_V
|
||||
|
||||
// Byte run length compression
|
||||
// Find a good tag
|
||||
for(x = 0; tmax && (x < 256); x++) {
|
||||
if(tag[x] < tmax) {
|
||||
tmax = tag[x];
|
||||
ctag = x;
|
||||
}
|
||||
}
|
||||
|
||||
// compress the data
|
||||
for(bp = buf, x = 0; (clen < blen) && (x < blen); x++) {
|
||||
// need at least 4 the same to be worth it
|
||||
// must compress tag (if it occurs)
|
||||
if((bp[x] == bp[x + 1]) && (bp[x] == bp[x + 2]) && (bp[x] == bp[x + 3]) ||
|
||||
(bp[x] == ctag)) {
|
||||
for(y = 1; (y < 255) && (bp[x] == bp[x + y]); y++)
|
||||
;
|
||||
*cp++ = ctag; // tag
|
||||
*cp++ = y; // length
|
||||
*cp++ = bp[x]; // byte
|
||||
x += y - 1;
|
||||
clen += 3;
|
||||
} else {
|
||||
*cp++ = bp[x];
|
||||
clen++;
|
||||
}
|
||||
}
|
||||
|
||||
// create struct
|
||||
fprintf(fh, "#include \"images.h\"\n\n");
|
||||
fprintf(fh, "const image_t img_%s = { %d, %d, ", name, img.w, img.h);
|
||||
|
||||
if(clen < blen) { // dump compressed?
|
||||
fprintf(
|
||||
fh,
|
||||
"true, %d, 0x%02X, { // orig:%d, comp:%.2f%%\n\t",
|
||||
clen,
|
||||
ctag,
|
||||
blen,
|
||||
100.0 - ((clen * 100.0) / blen));
|
||||
for(x = 0; x < clen; x++)
|
||||
if(x == clen - 1)
|
||||
fprintf(fh, "0x%02X\n}};\n", cmp[x]);
|
||||
else
|
||||
fprintf(fh, "0x%02X%s", cmp[x], (!((x + 1) % 16)) ? ",\n\t" : ", ");
|
||||
|
||||
} else { // dump UNcompressed
|
||||
fprintf(fh, "false, %d, 0, {\n\t", blen);
|
||||
for(x = 0; x < blen; x++)
|
||||
if(x == blen - 1)
|
||||
fprintf(fh, "0x%02X\n}};\n", buf[x]);
|
||||
else
|
||||
fprintf(fh, "0x%02X%s", buf[x], (!((x + 1) % 16)) ? ",\n\t" : ", ");
|
||||
}
|
||||
|
||||
bail:
|
||||
if(fh) fclose(fh);
|
||||
if(buf) free(buf);
|
||||
if(cmp) free(cmp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "images.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This will be the plot function out of your graphics library
|
||||
//
|
||||
#define PLOT(x, y, c) \
|
||||
do { \
|
||||
printf("%s", (c ? "#" : ".")); \
|
||||
if(x == img->w - 1) printf("\n"); \
|
||||
} while(0)
|
||||
|
||||
//+============================================================================
|
||||
// The pain we endure to avoid code duplication cleanly
|
||||
//
|
||||
#define PLOTBYTE(b) \
|
||||
do { \
|
||||
for(uint8_t m = 0x80; m; m >>= 1) { \
|
||||
PLOT(x, y, (b & m)); \
|
||||
if(((++x) == img->w) && !(x = 0) && ((++y) == img->h)) break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void show(const image_t* img) {
|
||||
// Some variables
|
||||
const uint8_t* bp = img->data;
|
||||
unsigned int x = 0;
|
||||
unsigned int y = 0;
|
||||
|
||||
// Compressed
|
||||
if(img->c) {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if(*bp == img->tag) {
|
||||
for(uint16_t c = 0; c < bp[1]; c++) PLOTBYTE(bp[2]);
|
||||
bp += 3 - 1;
|
||||
i += 3 - 1;
|
||||
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
PLOTBYTE(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Not compressed
|
||||
} else {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) PLOTBYTE(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
#undef PLOTBYTE
|
||||
|
||||
//+============================================================================
|
||||
int main(void) {
|
||||
show(&img_zzz);
|
||||
return 0;
|
||||
}
|
||||
31
applications/external/wiiec/application.fam
vendored
@@ -1,31 +0,0 @@
|
||||
# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
|
||||
|
||||
App(
|
||||
# --- App Info
|
||||
appid="wii_ec_anal",
|
||||
name="[WII] EC Analyser",
|
||||
# --- Entry point
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="wii_ec_anal",
|
||||
# --- Interaction
|
||||
cdefines=["APP_WII_EC_ANAL"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
# conflicts="",
|
||||
# sdk_headers="",
|
||||
# --- Run-time info
|
||||
stack_size=2 * 1024,
|
||||
# --- FAP details
|
||||
sources=["wii_*.c", "gfx/*.c"],
|
||||
# fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/",
|
||||
# fap_author="BlueChip",
|
||||
# fap_description="Wii Extension Controller Protocol Analyser",
|
||||
# fap_version=(1,0),
|
||||
fap_icon="WiiEC.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@csBlueChip",
|
||||
fap_weburl="https://github.com/csBlueChip/FlipperZero_WiiEC",
|
||||
fap_version="1.0",
|
||||
fap_description="Application to test Wii Extension Controllers.",
|
||||
)
|
||||
70
applications/external/wiiec/bc_logging.h
vendored
@@ -1,70 +0,0 @@
|
||||
#ifndef BC_LOGGING_H_
|
||||
#define BC_LOGGING_H_
|
||||
|
||||
#include <furi.h>
|
||||
#include "err.h" // appName
|
||||
|
||||
//! WARNING: There is a bug in Furi such that if you crank LOG_LEVEL up to 6=TRACE
|
||||
//! AND you have menu->settings->system->logLevel = trace
|
||||
//! THEN this program will cause the FZ to crash when the plugin exits!
|
||||
#define LOG_LEVEL 4
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time
|
||||
// ... LOG_LEVEL lets you limit it at COMPILE-time
|
||||
//
|
||||
// FURI logging has 6 levels (numbered 1 thru 6}
|
||||
// 1. None
|
||||
// 2. Errors FURI_LOG_E
|
||||
// 3. Warnings FURI_LOG_W
|
||||
// 4. Information FURI_LOG_I
|
||||
// 5. Debug FURI_LOG_D
|
||||
// 6. Trace FURI_LOG_T
|
||||
//
|
||||
// --> furi/core/log.h
|
||||
//
|
||||
|
||||
// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time
|
||||
// This lets you limit it at COMPILE-time
|
||||
#ifndef LOG_LEVEL
|
||||
#define LOG_LEVEL 6 // default = full logging
|
||||
#endif
|
||||
|
||||
#if(LOG_LEVEL < 2)
|
||||
#undef FURI_LOG_E
|
||||
#define FURI_LOG_E(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if(LOG_LEVEL < 3)
|
||||
#undef FURI_LOG_W
|
||||
#define FURI_LOG_W(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if(LOG_LEVEL < 4)
|
||||
#undef FURI_LOG_I
|
||||
#define FURI_LOG_I(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if(LOG_LEVEL < 5)
|
||||
#undef FURI_LOG_D
|
||||
#define FURI_LOG_D(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
#if(LOG_LEVEL < 6)
|
||||
#undef FURI_LOG_T
|
||||
#define FURI_LOG_T(tag, fmt, ...)
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Logging helper macros
|
||||
//
|
||||
#define ERROR(fmt, ...) FURI_LOG_E(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define WARN(fmt, ...) FURI_LOG_W(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define INFO(fmt, ...) FURI_LOG_I(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define DEBUG(fmt, ...) FURI_LOG_D(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define TRACE(fmt, ...) FURI_LOG_T(appName, fmt __VA_OPT__(, ) __VA_ARGS__)
|
||||
|
||||
#define ENTER TRACE("(+) %s", __func__)
|
||||
#define LEAVE TRACE("(-) %s", __func__)
|
||||
|
||||
#endif //BC_LOGGING_H_
|
||||
72
applications/external/wiiec/err.h
vendored
@@ -1,72 +0,0 @@
|
||||
// Avoid circular/nested/mulitple inclusion
|
||||
#ifndef ERR_H_
|
||||
#define ERR_H_
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
// Application name
|
||||
//
|
||||
static const char* const appName = "Wii_i2c"; //$ Name used in log files
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
// Error codes and messages
|
||||
//
|
||||
|
||||
// You should only ever (need to) edit this list
|
||||
// ...Watch out for extraneous whitespace after the terminating backslashes
|
||||
#define FOREACH_ES(esPrial) \
|
||||
/* The first line MUST define 'ERR_OK = 0' */ \
|
||||
esPrial(0, ERR_OK, "OK (no error)") \
|
||||
\
|
||||
esPrial(1, ERR_MALLOC_QUEUE, "malloc() fail - queue") esPrial( \
|
||||
2, \
|
||||
ERR_MALLOC_STATE, \
|
||||
"malloc() fail - state") esPrial(3, ERR_MALLOC_TEXT, "malloc() fail - text") \
|
||||
esPrial(4, ERR_MALLOC_VIEW, "malloc() fail - viewport") esPrial( \
|
||||
5, ERR_NO_MUTEX, "Cannot create mutex") esPrial(6, ERR_NO_GUI, "Cannot open GUI") \
|
||||
esPrial(7, ERR_NO_TIMER, "Cannot create timer") esPrial( \
|
||||
8, ERR_NO_NOTIFY, "Cannot acquire notifications handle") \
|
||||
\
|
||||
esPrial(10, ERR_MUTEX_BLOCK, "Mutex block failed") esPrial( \
|
||||
11, ERR_MUTEX_RELEASE, "Mutex release failed") \
|
||||
\
|
||||
esPrial(20, ERR_QUEUE_RTOS, "queue - Undefined RTOS error") \
|
||||
esPrial(21, DEBUG_QUEUE_TIMEOUT, "queue - Timeout") esPrial( \
|
||||
22, ERR_QUEUE_RESOURCE, "queue - Resource not available") \
|
||||
esPrial(23, ERR_QUEUE_BADPRM, "queue - Bad parameter") esPrial( \
|
||||
24, ERR_QUEUE_NOMEM, "queue - Out of memory") \
|
||||
esPrial(25, ERR_QUEUE_ISR, "queue - Banned in ISR") esPrial( \
|
||||
26, ERR_QUEUE_UNK, "queue - Unknown") \
|
||||
\
|
||||
esPrial(30, WARN_SCAN_START, "Scan - Already started") \
|
||||
esPrial(31, WARN_SCAN_STOP, "Scan - Already stopped") \
|
||||
esPrial( \
|
||||
32, \
|
||||
ERR_TIMER_START, \
|
||||
"Scan - Cannot start timer") \
|
||||
esPrial( \
|
||||
33, \
|
||||
ERR_TIMER_STOP, \
|
||||
"Scan - Cannot stop timer") //[EOT]
|
||||
|
||||
// Declare list extraction macros
|
||||
#define ES_ENUM(num, ename, string) ename = num,
|
||||
#define ES_STRING(num, ename, string) string "\r\n",
|
||||
|
||||
// Build the enum
|
||||
typedef enum err { FOREACH_ES(ES_ENUM) } err_t;
|
||||
|
||||
// You need to '#define ERR_C_' in precisely ONE source file
|
||||
#ifdef ERR_C_
|
||||
// Build the string list
|
||||
const char* const wii_errs[] = {FOREACH_ES(ES_STRING)};
|
||||
#else
|
||||
// Give access to string list
|
||||
extern const char* const wii_errs[];
|
||||
#endif
|
||||
|
||||
// This is a header file, clean up
|
||||
#undef ES_ENUM
|
||||
#undef ES_STRING
|
||||
#undef FOREACH_ES
|
||||
|
||||
#endif // ERR_H_
|
||||
137
applications/external/wiiec/gfx/images.c
vendored
@@ -1,137 +0,0 @@
|
||||
#include <gui/gui.h> // GUI (screen/keyboard) API
|
||||
|
||||
#include "images.h"
|
||||
|
||||
//----------------------------------------------------------------------------- ----------------------------------------
|
||||
static Canvas* _canvas;
|
||||
static uint8_t _tlx;
|
||||
static uint8_t _tly;
|
||||
|
||||
static uint8_t _x;
|
||||
static uint8_t _y;
|
||||
|
||||
static const image_t* _img;
|
||||
|
||||
static bool _blk;
|
||||
static Color _set;
|
||||
static Color _clr;
|
||||
|
||||
//+============================================================================
|
||||
static void _showByteSet(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if(b & m) // plot only SET bits
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
static void _showByteClr(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if(!(b & m)) // plot only CLR bits
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
static void _showByteAll(const uint8_t b) {
|
||||
for(uint8_t m = 0x80; m; m >>= 1) {
|
||||
if((!!(b & m)) ^ _blk) { // Change colour only when required
|
||||
canvas_set_color(_canvas, ((b & m) ? _set : _clr));
|
||||
_blk = !_blk;
|
||||
}
|
||||
canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y));
|
||||
if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break;
|
||||
}
|
||||
}
|
||||
|
||||
//+============================================================================
|
||||
// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK
|
||||
// SHOW_XOR - same as SET_BLACK
|
||||
// SHOW_SET_WHT - plot image pixels that are SET in WHITE
|
||||
// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK
|
||||
// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE
|
||||
// SHOW_ALL - plot all images pixels as they are
|
||||
// SHOW_ALL_INV - plot all images pixels inverted
|
||||
//
|
||||
void show(
|
||||
Canvas* const canvas,
|
||||
const uint8_t tlx,
|
||||
const uint8_t tly,
|
||||
const image_t* img,
|
||||
const showMode_t mode) {
|
||||
void (*fnShow)(const uint8_t) = NULL;
|
||||
|
||||
const uint8_t* bp = img->data;
|
||||
|
||||
// code size optimisation
|
||||
switch(mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
_set = ColorBlack;
|
||||
_clr = ColorWhite;
|
||||
break;
|
||||
|
||||
case SHOW_INV_:
|
||||
_set = ColorWhite;
|
||||
_clr = ColorBlack;
|
||||
break;
|
||||
|
||||
case SHOW_BLK_:
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
break;
|
||||
|
||||
case SHOW_WHT_:
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
break;
|
||||
}
|
||||
switch(mode & SHOW_INV_) {
|
||||
case SHOW_NRM_:
|
||||
case SHOW_INV_:
|
||||
fnShow = _showByteAll;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
_blk = 0;
|
||||
break;
|
||||
|
||||
case SHOW_BLK_:
|
||||
case SHOW_WHT_:
|
||||
switch(mode & SHOW_ALL_) {
|
||||
case SHOW_SET_:
|
||||
fnShow = _showByteSet;
|
||||
break;
|
||||
case SHOW_CLR_:
|
||||
fnShow = _showByteClr;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_check(fnShow);
|
||||
|
||||
// I want nested functions!
|
||||
_canvas = canvas;
|
||||
_img = img;
|
||||
_tlx = tlx;
|
||||
_tly = tly;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
|
||||
// Compressed
|
||||
if(img->c) {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) {
|
||||
// Compressed data? {tag, length, value}
|
||||
if(*bp == img->tag) {
|
||||
for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]);
|
||||
bp += 3 - 1;
|
||||
i += 3 - 1;
|
||||
|
||||
// Uncompressed byte
|
||||
} else {
|
||||
fnShow(*bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Not compressed
|
||||
} else {
|
||||
for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp);
|
||||
}
|
||||
}
|
||||