This commit is contained in:
Willy-JL
2023-02-08 04:05:46 +00:00
304 changed files with 13419 additions and 599 deletions

View File

@@ -10,7 +10,7 @@ on:
pull_request:
env:
TARGETS: f7
TARGETS: f7 f18
DEFAULT_TARGET: f7
FBT_TOOLCHAIN_PATH: /home/runner/work
@@ -57,8 +57,9 @@ jobs:
run: |
set -e
for TARGET in ${TARGETS}; do
./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
TARGET="$(echo "${TARGET}" | sed 's/f//')"; \
./fbt TARGET_HW=$TARGET copro_dist updater_package \
${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
done
- name: 'Move upload files'
@@ -154,6 +155,6 @@ jobs:
run: |
set -e
for TARGET in ${TARGETS}; do
./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
updater_package DEBUG=0 COMPACT=1
TARGET="$(echo "${TARGET}" | sed 's/f//')"; \
./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package
done

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.swp
*.swo
*.gdb_history

View File

@@ -11,5 +11,8 @@
"augustocdias.tasks-shell-input"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}
"unwantedRecommendations": [
"twxs.cmake",
"ms-vscode.cmake-tools"
]
}

View File

@@ -2,6 +2,7 @@ App(
appid="accessor",
name="Accessor",
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"],

View File

@@ -1,7 +1,7 @@
#include "bt_carrier_test.h"
#include "bt_test.h"
#include "bt_test_types.h"
#include "furi_hal_bt.h"
#include <furi_hal_bt.h>
struct BtCarrierTest {
BtTest* bt_test;

View File

@@ -1,7 +1,7 @@
#include "bt_packet_test.h"
#include "bt_test.h"
#include "bt_test_types.h"
#include "furi_hal_bt.h"
#include <furi_hal_bt.h>
struct BtPacketTest {
BtTest* bt_test;

View File

@@ -1,10 +1,11 @@
#include <file_browser_test_icons.h>
#include "file_browser_app_i.h"
#include "gui/modules/file_browser.h"
#include <furi.h>
#include <furi_hal.h>
#include <file_browser_test_icons.h>
#include <gui/modules/file_browser.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <furi.h>
#include <furi_hal.h>
static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);

View File

@@ -2,6 +2,7 @@ App(
appid="lfrfid_debug",
name="LF-RFID Debug",
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="lfrfid_debug_app",
requires=[
"gui",

View File

@@ -1,10 +1,11 @@
#include <archive/views/archive_browser_view.h>
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
#include "../views/archive_browser_view.h"
#include <core/common_defines.h>
#include <core/log.h>
#include "gui/modules/file_browser_worker.h"
#include <gui/modules/file_browser_worker.h>
#include <fap_loader/fap_loader_app.h>
#include <math.h>

View File

@@ -1,10 +1,13 @@
#pragma once
#include "../helpers/archive_files.h"
#include "../helpers/archive_favorites.h"
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <gui/modules/file_browser_worker.h>
#include <storage/storage.h>
#include "../helpers/archive_files.h"
#include "../helpers/archive_menu.h"

View File

@@ -1,6 +1,6 @@
#include "../bad_kb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <furi_hal_power.h>
#include <furi_hal_usb.h>
#include <storage/storage.h>
static bool bad_kb_file_select(BadKbApp* bad_kb) {

View File

@@ -1,7 +1,7 @@
#include "../bad_kb_script.h"
#include "../bad_kb_app_i.h"
#include "../views/bad_kb_view.h"
#include "furi_hal.h"
#include <furi_hal.h>
#include "toolbox/path.h"
void bad_kb_scene_work_button_callback(InputKey key, void* context) {

View File

@@ -25,6 +25,7 @@ GpioApp* gpio_app_alloc() {
GpioApp* app = malloc(sizeof(GpioApp));
app->gui = furi_record_open(RECORD_GUI);
app->gpio_items = gpio_items_alloc();
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
@@ -47,7 +48,7 @@ GpioApp* gpio_app_alloc() {
app->view_dispatcher,
GpioAppViewVarItemList,
variable_item_list_get_view(app->var_item_list));
app->gpio_test = gpio_test_alloc();
app->gpio_test = gpio_test_alloc(app->gpio_items);
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
@@ -105,6 +106,7 @@ void gpio_app_free(GpioApp* app) {
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
gpio_items_free(app->gpio_items);
free(app);
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include "gpio_app.h"
#include "gpio_item.h"
#include "gpio_items.h"
#include "scenes/gpio_scene.h"
#include "gpio_custom_event.h"
#include "usb_uart_bridge.h"
@@ -30,6 +30,7 @@ struct GpioApp {
VariableItem* var_item_flow;
GpioTest* gpio_test;
GpioUsbUart* gpio_usb_uart;
GPIOItems* gpio_items;
UsbUartBridge* usb_uart_bridge;
GpioI2CScanner* gpio_i2c_scanner;
GpioI2CSfp* gpio_i2c_sfp;

View File

@@ -1,51 +0,0 @@
#include "gpio_item.h"
#include <furi_hal_resources.h>
typedef struct {
const char* name;
const GpioPin* pin;
} GpioItem;
static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
{"1.2: PA7", &gpio_ext_pa7},
{"1.3: PA6", &gpio_ext_pa6},
{"1.4: PA4", &gpio_ext_pa4},
{"1.5: PB3", &gpio_ext_pb3},
{"1.6: PB2", &gpio_ext_pb2},
{"1.7: PC3", &gpio_ext_pc3},
{"2.7: PC1", &gpio_ext_pc1},
{"2.8: PC0", &gpio_ext_pc0},
};
void gpio_item_configure_pin(uint8_t index, GpioMode mode) {
furi_assert(index < GPIO_ITEM_COUNT);
furi_hal_gpio_write(gpio_item[index].pin, false);
furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
}
void gpio_item_configure_all_pins(GpioMode mode) {
for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
gpio_item_configure_pin(i, mode);
}
}
void gpio_item_set_pin(uint8_t index, bool level) {
furi_assert(index < GPIO_ITEM_COUNT);
furi_hal_gpio_write(gpio_item[index].pin, level);
}
void gpio_item_set_all_pins(bool level) {
for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
gpio_item_set_pin(i, level);
}
}
const char* gpio_item_get_pin_name(uint8_t index) {
furi_assert(index < GPIO_ITEM_COUNT + 1);
if(index == GPIO_ITEM_COUNT) {
return "ALL";
} else {
return gpio_item[index].name;
}
}

View File

@@ -1,15 +0,0 @@
#pragma once
#include <furi_hal_gpio.h>
#define GPIO_ITEM_COUNT 8
void gpio_item_configure_pin(uint8_t index, GpioMode mode);
void gpio_item_configure_all_pins(GpioMode mode);
void gpio_item_set_pin(uint8_t index, bool level);
void gpio_item_set_all_pins(bool level);
const char* gpio_item_get_pin_name(uint8_t index);

View File

@@ -0,0 +1,69 @@
#include "gpio_items.h"
#include <furi_hal_resources.h>
struct GPIOItems {
GpioPinRecord* pins;
size_t count;
};
GPIOItems* gpio_items_alloc() {
GPIOItems* items = malloc(sizeof(GPIOItems));
items->count = 0;
for(size_t i = 0; i < gpio_pins_count; i++) {
if(!gpio_pins[i].debug) {
items->count++;
}
}
items->pins = malloc(sizeof(GpioPinRecord) * items->count);
for(size_t i = 0; i < items->count; i++) {
if(!gpio_pins[i].debug) {
items->pins[i].pin = gpio_pins[i].pin;
items->pins[i].name = gpio_pins[i].name;
}
}
return items;
}
void gpio_items_free(GPIOItems* items) {
free(items->pins);
free(items);
}
uint8_t gpio_items_get_count(GPIOItems* items) {
return items->count;
}
void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) {
furi_assert(index < items->count);
furi_hal_gpio_write(items->pins[index].pin, false);
furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
}
void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) {
for(uint8_t i = 0; i < items->count; i++) {
gpio_items_configure_pin(items, i, mode);
}
}
void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) {
furi_assert(index < items->count);
furi_hal_gpio_write(items->pins[index].pin, level);
}
void gpio_items_set_all_pins(GPIOItems* items, bool level) {
for(uint8_t i = 0; i < items->count; i++) {
gpio_items_set_pin(items, i, level);
}
}
const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) {
furi_assert(index < items->count + 1);
if(index == items->count) {
return "ALL";
} else {
return items->pins[index].name;
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <furi_hal_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct GPIOItems GPIOItems;
GPIOItems* gpio_items_alloc();
void gpio_items_free(GPIOItems* items);
uint8_t gpio_items_get_count(GPIOItems* items);
void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode);
void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode);
void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level);
void gpio_items_set_all_pins(GPIOItems* items, bool level);
const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index);
#ifdef __cplusplus
}
#endif

View File

@@ -2,8 +2,9 @@
#define __GPIO_SCENE_START_H__
#include "../gpio_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <furi_hal_power.h>
#include <furi_hal_usb.h>
#include <dolphin/dolphin.h>
enum GpioItem {
GpioItemUsbUart,

View File

@@ -12,8 +12,9 @@ void gpio_scene_test_ok_callback(InputType type, void* context) {
}
void gpio_scene_test_on_enter(void* context) {
furi_assert(context);
GpioApp* app = context;
gpio_item_configure_all_pins(GpioModeOutputPushPull);
gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull);
gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);
}
@@ -25,6 +26,7 @@ bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) {
}
void gpio_scene_test_on_exit(void* context) {
UNUSED(context);
gpio_item_configure_all_pins(GpioModeAnalog);
furi_assert(context);
GpioApp* app = context;
gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog);
}

View File

@@ -1,6 +1,6 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi_hal.h"
#include <furi_hal.h>
typedef enum {
UsbUartLineIndexVcp,

View File

@@ -1,10 +1,10 @@
#include "usb_uart_bridge.h"
#include "furi_hal.h"
#include <furi_hal_usb_cdc.h>
#include "usb_cdc.h"
#include "cli/cli_vcp.h"
#include <cli/cli_vcp.h>
#include <cli/cli.h>
#include <toolbox/api_lock.h>
#include "cli/cli.h"
#include <furi_hal.h>
#include <furi_hal_usb_cdc.h>
#define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)

View File

@@ -1,6 +1,6 @@
#include <gui/elements.h>
#include "gpio_i2c_scanner.h"
#include "../gpio_item.h"
#include "../gpio_items.h"
#include <string.h>

View File

@@ -1,6 +1,6 @@
#include <gui/elements.h>
#include "gpio_i2c_sfp.h"
#include "../gpio_item.h"
#include "../gpio_items.h"
#include <string.h>

View File

@@ -1,5 +1,5 @@
#include "gpio_test.h"
#include "../gpio_item.h"
#include "../gpio_items.h"
#include <gui/elements.h>
@@ -11,6 +11,7 @@ struct GpioTest {
typedef struct {
uint8_t pin_idx;
GPIOItems* gpio_items;
} GpioTestModel;
static bool gpio_test_process_left(GpioTest* gpio_test);
@@ -25,7 +26,12 @@ static void gpio_test_draw_callback(Canvas* canvas, void* _model) {
elements_multiline_text_aligned(
canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin");
elements_multiline_text_aligned(
canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx));
canvas,
64,
32,
AlignCenter,
AlignTop,
gpio_items_get_pin_name(model->gpio_items, model->pin_idx));
}
static bool gpio_test_input_callback(InputEvent* event, void* context) {
@@ -64,7 +70,7 @@ static bool gpio_test_process_right(GpioTest* gpio_test) {
gpio_test->view,
GpioTestModel * model,
{
if(model->pin_idx < GPIO_ITEM_COUNT) {
if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
model->pin_idx++;
}
},
@@ -80,17 +86,17 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
GpioTestModel * model,
{
if(event->type == InputTypePress) {
if(model->pin_idx < GPIO_ITEM_COUNT) {
gpio_item_set_pin(model->pin_idx, true);
if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
gpio_items_set_pin(model->gpio_items, model->pin_idx, true);
} else {
gpio_item_set_all_pins(true);
gpio_items_set_all_pins(model->gpio_items, true);
}
consumed = true;
} else if(event->type == InputTypeRelease) {
if(model->pin_idx < GPIO_ITEM_COUNT) {
gpio_item_set_pin(model->pin_idx, false);
if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
gpio_items_set_pin(model->gpio_items, model->pin_idx, false);
} else {
gpio_item_set_all_pins(false);
gpio_items_set_all_pins(model->gpio_items, false);
}
consumed = true;
}
@@ -101,11 +107,15 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
return consumed;
}
GpioTest* gpio_test_alloc() {
GpioTest* gpio_test_alloc(GPIOItems* gpio_items) {
GpioTest* gpio_test = malloc(sizeof(GpioTest));
gpio_test->view = view_alloc();
view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));
with_view_model(
gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false);
view_set_context(gpio_test->view, gpio_test);
view_set_draw_callback(gpio_test->view, gpio_test_draw_callback);
view_set_input_callback(gpio_test->view, gpio_test_input_callback);

View File

@@ -1,11 +1,13 @@
#pragma once
#include "../gpio_items.h"
#include <gui/view.h>
typedef struct GpioTest GpioTest;
typedef void (*GpioTestOkCallback)(InputType type, void* context);
GpioTest* gpio_test_alloc();
GpioTest* gpio_test_alloc(GPIOItems* gpio_items);
void gpio_test_free(GpioTest* gpio_test);

View File

@@ -1,6 +1,6 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi_hal.h"
#include <furi_hal.h>
#include <gui/elements.h>
struct GpioUsbUart {

View File

@@ -2,6 +2,7 @@ App(
appid="ibutton",
name="iButton",
apptype=FlipperAppType.APP,
targets=["f7"],
entry_point="ibutton_app",
cdefines=["APP_IBUTTON"],
requires=[

View File

@@ -3,6 +3,7 @@ App(
name="Infrared",
apptype=FlipperAppType.APP,
entry_point="infrared_app",
targets=["f7"],
cdefines=["APP_INFRARED"],
requires=[
"gui",

View File

@@ -1,5 +1,5 @@
#include "../infrared_i.h"
#include "gui/canvas.h"
#include <gui/canvas.h>
typedef enum {
InfraredRpcStateIdle,

View File

@@ -1,11 +1,11 @@
#include "infrared_debug_view.h"
#include <stdlib.h>
#include <string.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <stdlib.h>
#include <string.h>
#define INFRARED_DEBUG_TEXT_LENGTH 64
struct InfraredDebugView {

View File

@@ -1,13 +1,15 @@
#include <core/check.h>
#include "furi_hal_resources.h"
#include "assets_icons.h"
#include "gui/canvas.h"
#include "gui/view.h"
#include "input/input.h"
#include <gui/elements.h>
#include <furi.h>
#include "infrared_progress_view.h"
#include "gui/modules/button_panel.h"
#include <assets_icons.h>
#include <gui/canvas.h>
#include <gui/view.h>
#include <gui/elements.h>
#include <gui/modules/button_panel.h>
#include <input/input.h>
#include <furi.h>
#include <furi_hal_resources.h>
#include <core/check.h>
#include <stdint.h>
struct InfraredProgressView {

View File

@@ -2,6 +2,7 @@ App(
appid="lfrfid",
name="125 kHz RFID",
apptype=FlipperAppType.APP,
targets=["f7"],
entry_point="lfrfid_app",
cdefines=["APP_LF_RFID"],
requires=[

View File

@@ -2,6 +2,7 @@ App(
appid="nfc",
name="NFC",
apptype=FlipperAppType.APP,
targets=["f7"],
entry_point="nfc_app",
cdefines=["APP_NFC"],
requires=[

View File

@@ -1,5 +1,5 @@
#include "nfc_i.h"
#include "furi_hal_nfc.h"
#include <furi_hal_nfc.h>
#include <dolphin/dolphin.h>
bool nfc_custom_event_callback(void* context, uint32_t event) {

View File

@@ -2,6 +2,7 @@ App(
appid="subghz",
name="Sub-GHz",
apptype=FlipperAppType.APP,
targets=["f7"],
entry_point="subghz_app",
cdefines=["APP_SUBGHZ"],
requires=[

View File

@@ -1,7 +1,7 @@
#include "../u2f_app_i.h"
#include "../views/u2f_view.h"
#include <dolphin/dolphin.h>
#include "furi_hal.h"
#include <furi_hal.h>
#include "../u2f.h"
#define U2F_REQUEST_TIMEOUT 500

View File

@@ -0,0 +1,15 @@
App(
appid="Brainfuck",
name="Brainfuck",
apptype=FlipperAppType.EXTERNAL,
entry_point="brainfuck_app",
requires=[
"storage",
"gui",
],
stack_size=8 * 1024,
fap_icon="bfico.png",
fap_category="Misc",
fap_icon_assets="icons",
fap_icon_assets_symbol="brainfuck",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,149 @@
#include "brainfuck_i.h"
/*
Due to the lack of documentation on the flipper i copied the picopass app,
ripped its insides out and used its hollow corpse to build this app inside of.
i dont know how this stuff works and after 6 hours of trying to learn it, i dont care
*/
bool brainfuck_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
BFApp* brainfuck = context;
return scene_manager_handle_custom_event(brainfuck->scene_manager, event);
}
bool brainfuck_back_event_callback(void* context) {
furi_assert(context);
BFApp* brainfuck = context;
return scene_manager_handle_back_event(brainfuck->scene_manager);
}
BFApp* brainfuck_alloc() {
BFApp* brainfuck = malloc(sizeof(BFApp));
brainfuck->dataSize = 0;
brainfuck->view_dispatcher = view_dispatcher_alloc();
brainfuck->scene_manager = scene_manager_alloc(&brainfuck_scene_handlers, brainfuck);
view_dispatcher_enable_queue(brainfuck->view_dispatcher);
view_dispatcher_set_event_callback_context(brainfuck->view_dispatcher, brainfuck);
view_dispatcher_set_custom_event_callback(
brainfuck->view_dispatcher, brainfuck_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
brainfuck->view_dispatcher, brainfuck_back_event_callback);
// Open GUI record
brainfuck->gui = furi_record_open(RECORD_GUI);
view_dispatcher_attach_to_gui(
brainfuck->view_dispatcher, brainfuck->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
brainfuck->notifications = furi_record_open(RECORD_NOTIFICATION);
// Submenu
brainfuck->submenu = submenu_alloc();
view_dispatcher_add_view(
brainfuck->view_dispatcher, brainfuckViewMenu, submenu_get_view(brainfuck->submenu));
// Popup
brainfuck->popup = popup_alloc();
view_dispatcher_add_view(
brainfuck->view_dispatcher, brainfuckViewPopup, popup_get_view(brainfuck->popup));
// Text Input
brainfuck->text_input = text_input_alloc();
view_dispatcher_add_view(
brainfuck->view_dispatcher,
brainfuckViewTextInput,
text_input_get_view(brainfuck->text_input));
// Textbox
brainfuck->text_box = text_box_alloc();
view_dispatcher_add_view(
brainfuck->view_dispatcher, brainfuckViewTextBox, text_box_get_view(brainfuck->text_box));
brainfuck->text_box_store = furi_string_alloc();
// Dev environment
brainfuck->BF_dev_env = bf_dev_env_alloc(brainfuck);
view_dispatcher_add_view(
brainfuck->view_dispatcher, brainfuckViewDev, bf_dev_env_get_view(brainfuck->BF_dev_env));
// File path
brainfuck->BF_file_path = furi_string_alloc();
return brainfuck;
}
void brainfuck_free(BFApp* brainfuck) {
furi_assert(brainfuck);
// Submenu
view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewMenu);
submenu_free(brainfuck->submenu);
// Popup
view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewPopup);
popup_free(brainfuck->popup);
// TextInput
view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextInput);
text_input_free(brainfuck->text_input);
// TextBox
view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextBox);
text_box_free(brainfuck->text_box);
furi_string_free(brainfuck->text_box_store);
//dev env
view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewDev);
bf_dev_env_free(brainfuck->BF_dev_env);
// View Dispatcher
view_dispatcher_free(brainfuck->view_dispatcher);
// Scene Manager
scene_manager_free(brainfuck->scene_manager);
// GUI
furi_record_close(RECORD_GUI);
brainfuck->gui = NULL;
// Notifications
furi_record_close(RECORD_NOTIFICATION);
brainfuck->notifications = NULL;
free(brainfuck);
}
void brainfuck_show_loading_popup(void* context, bool show) {
BFApp* brainfuck = 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(brainfuck->view_dispatcher, brainfuckViewLoading);
} else {
// Restore default timer priority
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
}
}
int32_t brainfuck_app(void* p) {
UNUSED(p);
BFApp* brainfuck = brainfuck_alloc();
if(!brainfuck) {
return 0;
}
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_simply_mkdir(storage, "/ext/brainfuck");
scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart);
view_dispatcher_run(brainfuck->view_dispatcher);
brainfuck_free(brainfuck);
return 0;
}

View File

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

View File

@@ -0,0 +1,89 @@
#pragma once
typedef struct BFDevEnv BFDevEnv;
typedef struct BFExecEnv BFExecEnv;
typedef unsigned char byte;
#include "brainfuck.h"
#include "worker.h"
#include <furi.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <notification/notification_messages.h>
#include <gui/modules/submenu.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/text_box.h>
#include <dialogs/dialogs.h>
#include <input/input.h>
#include "scenes/brainfuck_scene.h"
#include "views/bf_dev_env.h"
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <brainfuck_icons.h>
#include <storage/storage.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#include <toolbox/stream/file_stream.h>
#include <notification/notification_messages.h>
#include <notification/notification_app.h>
#define BF_INST_BUFFER_SIZE 2048
#define BF_OUTPUT_SIZE 512
#define BF_STACK_INITIAL_SIZE 128
#define BF_INPUT_BUFFER_SIZE 64
#define BF_STACK_STEP_SIZE 32
enum brainfuckCustomEvent {
// Reserve first 100 events for button types and indexes, starting from 0
brainfuckCustomEventReserved = 100,
brainfuckCustomEventViewExit,
brainfuckCustomEventWorkerExit,
brainfuckCustomEventByteInputDone,
brainfuckCustomEventTextInputDone,
};
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
struct BFApp {
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notifications;
SceneManager* scene_manager;
Submenu* submenu;
Popup* popup;
TextInput* text_input;
TextBox* text_box;
FuriString* text_box_store;
FuriString* BF_file_path;
BFDevEnv* BF_dev_env;
int dataSize;
char dataBuffer[BF_INST_BUFFER_SIZE];
char inputBuffer[BF_INPUT_BUFFER_SIZE];
};
typedef enum {
brainfuckViewMenu,
brainfuckViewPopup,
brainfuckViewLoading,
brainfuckViewTextInput,
brainfuckViewTextBox,
brainfuckViewWidget,
brainfuckViewDev,
brainfuckViewExec,
} brainfuckView;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,30 @@
#include "brainfuck_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const brainfuck_on_enter_handlers[])(void*) = {
#include "brainfuck_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 brainfuck_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "brainfuck_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 brainfuck_on_exit_handlers[])(void* context) = {
#include "brainfuck_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers brainfuck_scene_handlers = {
.on_enter_handlers = brainfuck_on_enter_handlers,
.on_event_handlers = brainfuck_on_event_handlers,
.on_exit_handlers = brainfuck_on_exit_handlers,
.scene_num = brainfuckSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) brainfuckScene##id,
typedef enum {
#include "brainfuck_scene_config.h"
brainfuckSceneNum,
} brainfuckScene;
#undef ADD_SCENE
extern const SceneManagerHandlers brainfuck_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "brainfuck_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 "brainfuck_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 "brainfuck_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,6 @@
ADD_SCENE(brainfuck, start, Start)
ADD_SCENE(brainfuck, file_select, FileSelect)
ADD_SCENE(brainfuck, file_create, FileCreate)
ADD_SCENE(brainfuck, dev_env, DevEnv)
ADD_SCENE(brainfuck, exec_env, ExecEnv)
ADD_SCENE(brainfuck, set_input, SetInput)

View File

@@ -0,0 +1,16 @@
#include "../brainfuck_i.h"
void brainfuck_scene_dev_env_on_enter(void* context) {
BFApp* app = context;
view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewDev);
}
bool brainfuck_scene_dev_env_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void brainfuck_scene_dev_env_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,16 @@
#include "../brainfuck_i.h"
void brainfuck_scene_exec_env_on_enter(void* context) {
BFApp* app = context;
view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextBox);
}
bool brainfuck_scene_exec_env_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void brainfuck_scene_exec_env_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,50 @@
#include "../brainfuck_i.h"
void file_name_text_input_callback(void* context) {
BFApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone);
}
char tmpName[64] = {};
byte empty[1] = {0x00};
void brainfuck_scene_file_create_on_enter(void* context) {
BFApp* app = context;
TextInput* text_input = app->text_input;
text_input_set_header_text(text_input, "New script name");
text_input_set_result_callback(
text_input, file_name_text_input_callback, app, tmpName, 64, true);
view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput);
}
bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event) {
BFApp* app = context;
UNUSED(app);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == brainfuckCustomEventTextInputDone) {
furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName);
//remove old file
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_simply_remove(storage, furi_string_get_cstr(app->BF_file_path));
//save new file
Stream* stream = buffered_file_stream_alloc(storage);
buffered_file_stream_open(
stream, furi_string_get_cstr(app->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS);
stream_write(stream, (const uint8_t*)empty, 1);
buffered_file_stream_close(stream);
//scene_manager_next_scene(app->scene_manager, brainfuckSceneFileSelect);
scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv);
}
}
return consumed;
}
void brainfuck_scene_file_create_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,34 @@
#include "../brainfuck_i.h"
void brainfuck_scene_file_select_on_enter(void* context) {
BFApp* app = context;
DialogsApp* dialogs = furi_record_open("dialogs");
FuriString* path;
path = furi_string_alloc();
furi_string_set(path, "/ext/brainfuck");
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico);
browser_options.base_path = "/ext/brainfuck";
browser_options.hide_ext = false;
bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options);
if(selected) {
furi_string_set(app->BF_file_path, path);
scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv);
} else {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneStart);
}
}
bool brainfuck_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void brainfuck_scene_file_select_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,35 @@
#include "../brainfuck_i.h"
void set_input_text_input_callback(void* context) {
BFApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone);
}
void brainfuck_scene_set_input_on_enter(void* context) {
BFApp* app = context;
TextInput* text_input = app->text_input;
text_input_set_header_text(text_input, "Edit input buffer");
text_input_set_result_callback(
text_input, set_input_text_input_callback, app, app->inputBuffer, 64, true);
view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput);
}
bool brainfuck_scene_set_input_on_event(void* context, SceneManagerEvent event) {
BFApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == brainfuckCustomEventTextInputDone) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, brainfuckSceneDevEnv);
}
}
return consumed;
}
void brainfuck_scene_set_input_on_exit(void* context) {
BFApp* app = context;
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneDevEnv);
}

View File

@@ -0,0 +1,55 @@
#include "../brainfuck_i.h"
enum SubmenuIndex {
SubmenuIndexNew,
SubmenuIndexOpen,
SubmenuIndexAbout,
};
void brainfuck_scene_start_submenu_callback(void* context, uint32_t index) {
BFApp* brainfuck = context;
view_dispatcher_send_custom_event(brainfuck->view_dispatcher, index);
}
void brainfuck_scene_start_on_enter(void* context) {
BFApp* brainfuck = context;
Submenu* submenu = brainfuck->submenu;
submenu_add_item(
submenu, "New", SubmenuIndexNew, brainfuck_scene_start_submenu_callback, brainfuck);
submenu_add_item(
submenu, "Open", SubmenuIndexOpen, brainfuck_scene_start_submenu_callback, brainfuck);
submenu_add_item(
submenu, "About", SubmenuIndexAbout, brainfuck_scene_start_submenu_callback, brainfuck);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(brainfuck->scene_manager, brainfuckSceneStart));
view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewMenu);
}
bool brainfuck_scene_start_on_event(void* context, SceneManagerEvent event) {
BFApp* brainfuck = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexNew) {
scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileCreate);
consumed = true;
} else if(event.event == SubmenuIndexOpen) {
scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexAbout) {
text_box_set_text(
brainfuck->text_box,
"FlipperBrainfuck\n\nAn F0 brainfuck intepretor\nBy github.com/Nymda");
scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneExecEnv);
consumed = true;
}
scene_manager_set_scene_state(brainfuck->scene_manager, brainfuckSceneStart, event.event);
}
return consumed;
}
void brainfuck_scene_start_on_exit(void* context) {
BFApp* brainfuck = context;
submenu_reset(brainfuck->submenu);
}

View File

@@ -0,0 +1,419 @@
#include "bf_dev_env.h"
#include <gui/elements.h>
typedef struct BFDevEnv {
View* view;
DevEnvOkCallback callback;
void* context;
BFApp* appDev;
} BFDevEnv;
typedef struct {
uint32_t row;
uint32_t col;
} BFDevEnvModel;
typedef struct {
int up;
int down;
int left;
int right;
} bMapping;
static bool bf_dev_process_up(BFDevEnv* devEnv);
static bool bf_dev_process_down(BFDevEnv* devEnv);
static bool bf_dev_process_left(BFDevEnv* devEnv);
static bool bf_dev_process_right(BFDevEnv* devEnv);
static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event);
BFApp* appDev;
FuriThread* workerThread;
char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00};
int selectedButton = 0;
int saveNotifyCountdown = 0;
int execCountdown = 0;
char dspLine0[25] = {};
char dspLine1[25] = {};
char dspLine2[25] = {};
static bMapping buttonMappings[12] = {
{8, 8, 7, 1}, //0
{8, 8, 0, 2}, //1
{9, 9, 1, 3}, //2
{9, 9, 2, 4}, //3
{10, 10, 3, 5}, //4
{10, 10, 4, 6}, //5
{11, 11, 5, 7}, //6
{11, 11, 6, 0}, //7
{0, 0, 11, 9}, //8
{3, 3, 8, 10}, //9
{5, 5, 9, 11}, //10
{6, 6, 10, 8} //11
};
#define BT_X 14
#define BT_Y 14
static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl) {
UNUSED(lbl);
if(selected) {
canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3);
canvas_invert_color(canvas);
canvas_set_font(canvas, FontBatteryPercent);
canvas_draw_str_aligned(
canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl);
canvas_invert_color(canvas);
} else {
canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3);
canvas_invert_color(canvas);
canvas_draw_rbox(canvas, x + 2, y - 1, BT_X - 2, BT_Y - 1, 3);
canvas_invert_color(canvas);
canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3);
canvas_set_font(canvas, FontBatteryPercent);
canvas_draw_str_aligned(
canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl);
}
}
void bf_save_changes() {
//remove old file
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path));
//save new file
Stream* stream = buffered_file_stream_alloc(storage);
buffered_file_stream_open(
stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS);
stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize);
buffered_file_stream_close(stream);
}
static void bf_dev_draw_callback(Canvas* canvas, void* _model) {
UNUSED(_model);
if(saveNotifyCountdown > 0) {
canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED");
saveNotifyCountdown--;
return;
}
bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0
bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1
bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2
bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3
bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0
bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1
bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2
bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3
//backspace, input, run, save
canvas_draw_icon(
canvas,
1,
52,
(selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11);
canvas_draw_icon(
canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11);
canvas_draw_icon(
canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11);
canvas_draw_icon(
canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11);
if(saveNotifyCountdown > 0) {
canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5);
saveNotifyCountdown--;
}
//textbox
//grossly overcomplicated. not fixing it.
canvas_draw_rframe(canvas, 1, 1, 126, 33, 2);
canvas_set_font(canvas, FontBatteryPercent);
int dbOffset = 0;
if(appDev->dataSize > 72) {
dbOffset = (appDev->dataSize - 72);
}
memset(dspLine0, 0x00, 25);
memset(dspLine1, 0x00, 25);
memset(dspLine2, 0x00, 25);
int tpM = 0;
int tp0 = 0;
int tp1 = 0;
int tp2 = 0;
for(int p = dbOffset; p < appDev->dataSize; p++) {
if(tpM < 24 * 1) {
dspLine0[tp0] = appDev->dataBuffer[p];
tp0++;
} else if(tpM < 24 * 2) {
dspLine1[tp1] = appDev->dataBuffer[p];
tp1++;
} else if(tpM < 24 * 3) {
dspLine2[tp2] = appDev->dataBuffer[p];
tp2++;
}
tpM++;
}
canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0);
canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1);
canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2);
}
static bool bf_dev_input_callback(InputEvent* event, void* context) {
furi_assert(context);
BFDevEnv* devEnv = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
consumed = bf_dev_process_right(devEnv);
} else if(event->key == InputKeyLeft) {
consumed = bf_dev_process_left(devEnv);
} else if(event->key == InputKeyUp) {
consumed = bf_dev_process_up(devEnv);
} else if(event->key == InputKeyDown) {
consumed = bf_dev_process_down(devEnv);
}
} else if(event->key == InputKeyOk) {
consumed = bf_dev_process_ok(devEnv, event);
}
return consumed;
}
static bool bf_dev_process_up(BFDevEnv* devEnv) {
UNUSED(devEnv);
selectedButton = buttonMappings[selectedButton].up;
return true;
}
static bool bf_dev_process_down(BFDevEnv* devEnv) {
UNUSED(devEnv);
selectedButton = buttonMappings[selectedButton].down;
return true;
}
static bool bf_dev_process_left(BFDevEnv* devEnv) {
UNUSED(devEnv);
selectedButton = buttonMappings[selectedButton].left;
return true;
}
static bool bf_dev_process_right(BFDevEnv* devEnv) {
UNUSED(devEnv);
selectedButton = buttonMappings[selectedButton].right;
return true;
}
static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) {
UNUSED(devEnv);
UNUSED(event);
if(event->type != InputTypePress) {
return false;
}
switch(selectedButton) {
case 0: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = '+';
appDev->dataSize++;
}
break;
}
case 1: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = '-';
appDev->dataSize++;
}
break;
}
case 2: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = '<';
appDev->dataSize++;
}
break;
}
case 3: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = '>';
appDev->dataSize++;
}
break;
}
case 4: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = '[';
appDev->dataSize++;
}
break;
}
case 5: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = ']';
appDev->dataSize++;
}
break;
}
case 6: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = '.';
appDev->dataSize++;
}
break;
}
case 7: {
if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
appDev->dataBuffer[appDev->dataSize] = ',';
appDev->dataSize++;
}
break;
}
case 8: {
if(appDev->dataSize > 0) {
appDev->dataSize--;
appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00;
}
break;
}
case 9: {
scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput);
break;
}
case 10: {
if(getStatus() != 0) {
killThread();
furi_thread_join(workerThread);
}
bf_save_changes();
initWorker(appDev);
text_box_set_focus(appDev->text_box, TextBoxFocusEnd);
text_box_set_text(appDev->text_box, workerGetOutput());
workerThread = furi_thread_alloc_ex("Worker", 2048, (void*)beginWorker, NULL);
furi_thread_start(workerThread);
scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv);
break;
}
case 11: {
bf_save_changes();
saveNotifyCountdown = 3;
break;
}
}
bool consumed = false;
return consumed;
}
static void bf_dev_enter_callback(void* context) {
furi_assert(context);
BFDevEnv* devEnv = context;
with_view_model(
devEnv->view,
BFDevEnvModel * model,
{
model->col = 0;
model->row = 0;
},
true);
appDev = devEnv->appDev;
selectedButton = 0;
//exit the running thread if required
if(getStatus() != 0) {
killThread();
furi_thread_join(workerThread);
}
//clear the bf instruction buffer
memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char));
//open the file
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* stream = buffered_file_stream_alloc(storage);
buffered_file_stream_open(
stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING);
//read into the buffer
appDev->dataSize = stream_size(stream);
stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize);
buffered_file_stream_close(stream);
//replaces any invalid characters with an underscore. strips out newlines, comments, etc
for(int i = 0; i < appDev->dataSize; i++) {
if(!strchr(bfChars, appDev->dataBuffer[i])) {
appDev->dataBuffer[i] = '_';
}
}
//find the end of the file to begin editing
int tptr = 0;
while(appDev->dataBuffer[tptr] != 0x00) {
tptr++;
}
appDev->dataSize = tptr;
}
BFDevEnv* bf_dev_env_alloc(BFApp* appDev) {
BFDevEnv* devEnv = malloc(sizeof(BFDevEnv));
devEnv->view = view_alloc();
devEnv->appDev = appDev;
view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel));
with_view_model(
devEnv->view,
BFDevEnvModel * model,
{
model->col = 0;
model->row = 0;
},
true);
view_set_context(devEnv->view, devEnv);
view_set_draw_callback(devEnv->view, bf_dev_draw_callback);
view_set_input_callback(devEnv->view, bf_dev_input_callback);
view_set_enter_callback(devEnv->view, bf_dev_enter_callback);
return devEnv;
}
void bf_dev_env_free(BFDevEnv* devEnv) {
if(getStatus() != 0) {
killThread();
furi_thread_join(workerThread);
}
furi_assert(devEnv);
view_free(devEnv->view);
free(devEnv);
}
View* bf_dev_env_get_view(BFDevEnv* devEnv) {
furi_assert(devEnv);
return devEnv->view;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "../brainfuck_i.h"
#include <gui/view.h>
typedef void (*DevEnvOkCallback)(InputType type, void* context);
BFDevEnv* bf_dev_env_alloc(BFApp* application);
void bf_dev_set_file_path(FuriString* path);
void bf_dev_env_free(BFDevEnv* devEnv);
View* bf_dev_env_get_view(BFDevEnv* devEnv);
void bf_dev_env_set_ok(BFDevEnv* devEnv, DevEnvOkCallback callback, void* context);

View File

@@ -0,0 +1,276 @@
#include "worker.h"
bool killswitch = false;
int status = 0; //0: idle, 1: running, 2: failure
char* inst = 0;
int instCount = 0;
int instPtr = 0;
int runOpCount = 0;
char* wOutput = 0;
int wOutputPtr = 0;
char* wInput = 0;
int wInputPtr = 0;
uint8_t* bfStack = 0;
int stackPtr = 0;
int stackSize = BF_STACK_INITIAL_SIZE;
int stackSizeReal = 0;
BFApp* wrkrApp = 0;
void killThread() {
killswitch = true;
}
bool validateInstPtr() {
if(instPtr > instCount || instPtr < 0) {
return false;
}
return true;
}
bool validateStackPtr() {
if(stackPtr > stackSize || stackPtr < 0) {
return false;
}
return true;
}
char* workerGetOutput() {
return wOutput;
}
int getStackSize() {
return stackSizeReal;
}
int getOpCount() {
return runOpCount;
}
int getStatus() {
return status;
}
void initWorker(BFApp* app) {
wrkrApp = app;
//rebuild output
if(wOutput) {
free(wOutput);
}
wOutput = (char*)malloc(BF_OUTPUT_SIZE);
wOutputPtr = 0;
//rebuild stack
if(bfStack) {
free(bfStack);
}
bfStack = (uint8_t*)malloc(BF_STACK_INITIAL_SIZE);
memset(bfStack, 0x00, BF_STACK_INITIAL_SIZE);
stackSize = BF_STACK_INITIAL_SIZE;
stackSizeReal = 0;
stackPtr = 0;
//set instructions
inst = wrkrApp->dataBuffer;
instCount = wrkrApp->dataSize;
instPtr = 0;
runOpCount = 0;
//set input
wInput = wrkrApp->inputBuffer;
wInputPtr = 0;
//set status
status = 0;
}
void rShift() {
runOpCount++;
stackPtr++;
if(!validateStackPtr()) {
status = 2;
return;
}
while(stackPtr > stackSize) {
stackSize += BF_STACK_STEP_SIZE;
void* tmp = realloc(bfStack, stackSize);
if(!tmp) {
status = 2;
return;
}
memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE);
bfStack = (uint8_t*)tmp;
};
if(stackPtr > stackSizeReal) {
stackSizeReal = stackPtr;
}
}
void lShift() {
runOpCount++;
stackPtr--;
if(!validateStackPtr()) {
status = 2;
return;
}
}
void inc() {
runOpCount++;
if(!validateStackPtr()) {
status = 2;
return;
}
bfStack[stackPtr]++;
}
void dec() {
runOpCount++;
if(!validateStackPtr()) {
status = 2;
return;
}
bfStack[stackPtr]--;
}
void print() {
runOpCount++;
wOutput[wOutputPtr] = bfStack[stackPtr];
wOutputPtr++;
if(wOutputPtr > (BF_OUTPUT_SIZE - 1)) {
wOutputPtr = 0;
}
}
void input() {
runOpCount++;
bfStack[stackPtr] = (uint8_t)wInput[wInputPtr];
if(wInput[wInputPtr] == 0x00 || wInputPtr >= 64) {
wInputPtr = 0;
} else {
wInputPtr++;
}
}
void loop() {
runOpCount++;
if(bfStack[stackPtr] == 0) {
int loopCount = 1;
while(loopCount > 0) {
instPtr++;
if(!validateInstPtr()) {
status = 2;
return;
}
if(inst[instPtr] == '[') {
loopCount++;
} else if(inst[instPtr] == ']') {
loopCount--;
}
}
}
}
void endLoop() {
runOpCount++;
if(bfStack[stackPtr] != 0) {
int loopCount = 1;
while(loopCount > 0) {
instPtr--;
if(!validateInstPtr()) {
status = 2;
return;
}
if(inst[instPtr] == ']') {
loopCount++;
} else if(inst[instPtr] == '[') {
loopCount--;
}
}
}
}
static const NotificationSequence led_on = {
&message_blue_255,
&message_do_not_reset,
NULL,
};
static const NotificationSequence led_off = {
&message_green_0,
NULL,
};
void beginWorker() {
status = 1;
while(inst[instPtr] != 0x00) {
if(runOpCount % 500 == 0) {
text_box_set_text(wrkrApp->text_box, workerGetOutput());
notification_message(wrkrApp->notifications, &led_on);
}
if(status == 2) {
status = 0;
break;
}
if(killswitch) {
status = 0;
killswitch = false;
break;
}
switch(inst[instPtr]) {
case '>':
rShift();
break;
case '<':
lShift();
break;
case '+':
inc();
break;
case '-':
dec();
break;
case '.':
print();
break;
case ',':
input();
break;
case '[':
loop();
break;
case ']':
endLoop();
break;
default:
break;
}
instPtr++;
if(!validateInstPtr()) {
status = 0;
break;
}
}
notification_message(wrkrApp->notifications, &led_off);
text_box_set_text(wrkrApp->text_box, workerGetOutput());
status = 0;
}

View File

@@ -0,0 +1,9 @@
#include "brainfuck_i.h"
void initWorker(BFApp* application);
char* workerGetOutput();
int getStackSize();
int getOpCount();
int getStatus();
void beginWorker();
void killThread();

View File

@@ -0,0 +1,13 @@
App(
appid="Geiger_Coutner",
name="Geiger Counter",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipper_geiger_app",
cdefines=["APP_GEIGER"],
requires=[
"gui",
],
stack_size=1 * 1024,
fap_icon="geiger.png",
fap_category="GPIO",
)

View File

@@ -0,0 +1,227 @@
// CC0 1.0 Universal (CC0 1.0)
// Public Domain Dedication
// https://github.com/nmrr
#include <stdio.h>
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <furi_hal_random.h>
#include <furi_hal_pwm.h>
#include <furi_hal_power.h>
#define SCREEN_SIZE_X 128
#define SCREEN_SIZE_Y 64
// FOR J305 GEIGER TUBE
#define CONVERSION_FACTOR 0.0081
typedef enum {
EventTypeInput,
ClockEventTypeTick,
EventGPIO,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} EventApp;
typedef struct {
uint32_t cps, cpm;
uint32_t line[SCREEN_SIZE_X / 2];
float coef;
uint8_t data;
} mutexStruct;
static void draw_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
mutexStruct displayStruct;
mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx);
memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct));
release_mutex((ValueMutex*)ctx, geigerMutex);
char buffer[32];
if(displayStruct.data == 0)
snprintf(
buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm);
else if(displayStruct.data == 1)
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.2f uSv/h",
displayStruct.cps,
((double)displayStruct.cpm * (double)CONVERSION_FACTOR));
else
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.2f mSv/y",
displayStruct.cps,
(((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76);
for(int i = 0; i < SCREEN_SIZE_X; i += 2) {
float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef);
canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y);
canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y);
}
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer);
}
static void input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
EventApp event = {.type = EventTypeInput, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void clock_tick(void* ctx) {
furi_assert(ctx);
uint32_t randomNumber = furi_hal_random_get();
randomNumber &= 0xFFF;
if(randomNumber == 0) randomNumber = 1;
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50);
FuriMessageQueue* queue = ctx;
EventApp event = {.type = ClockEventTypeTick};
furi_message_queue_put(queue, &event, 0);
}
static void gpiocallback(void* ctx) {
furi_assert(ctx);
FuriMessageQueue* queue = ctx;
EventApp event = {.type = EventGPIO};
furi_message_queue_put(queue, &event, 0);
}
int32_t flipper_geiger_app() {
EventApp event;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInterruptFall, GpioPullUp, GpioSpeedVeryHigh);
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 5, 50);
mutexStruct mutexVal;
mutexVal.cps = 0;
mutexVal.cpm = 0;
for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0;
mutexVal.coef = 1;
mutexVal.data = 0;
uint32_t counter = 0;
ValueMutex state_mutex;
init_mutex(&state_mutex, &mutexVal, sizeof(mutexVal));
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, &state_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, 1000);
// ENABLE 5V pin
furi_hal_power_enable_otg();
while(1) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);
uint8_t screenRefresh = 0;
if(event_status == FuriStatusOk) {
if(event.type == EventTypeInput) {
if(event.input.key == InputKeyBack) {
break;
} else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) {
counter = 0;
mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
geigerMutex->cps = 0;
geigerMutex->cpm = 0;
for(int i = 0; i < SCREEN_SIZE_X / 2; i++) geigerMutex->line[i] = 0;
screenRefresh = 1;
release_mutex(&state_mutex, geigerMutex);
} else if((event.input.key == InputKeyLeft &&
event.input.type == InputTypeShort)) {
mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
if(geigerMutex->data != 0)
geigerMutex->data--;
else
geigerMutex->data = 2;
screenRefresh = 1;
release_mutex(&state_mutex, geigerMutex);
} else if((event.input.key == InputKeyRight &&
event.input.type == InputTypeShort)) {
mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
if(geigerMutex->data != 2)
geigerMutex->data++;
else
geigerMutex->data = 0;
screenRefresh = 1;
release_mutex(&state_mutex, geigerMutex);
}
} else if(event.type == ClockEventTypeTick) {
mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++)
geigerMutex->line[SCREEN_SIZE_X / 2 - 1 - i] =
geigerMutex->line[SCREEN_SIZE_X / 2 - 2 - i];
geigerMutex->line[0] = counter;
geigerMutex->cps = counter;
counter = 0;
geigerMutex->cpm = geigerMutex->line[0];
uint32_t max = geigerMutex->line[0];
for(int i = 1; i < SCREEN_SIZE_X / 2; i++) {
if(i < 60) geigerMutex->cpm += geigerMutex->line[i];
if(geigerMutex->line[i] > max) max = geigerMutex->line[i];
}
if(max > 0)
geigerMutex->coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max);
else
geigerMutex->coef = 1;
screenRefresh = 1;
release_mutex(&state_mutex, geigerMutex);
} else if(event.type == EventGPIO) {
counter++;
}
}
if(screenRefresh == 1) view_port_update(view_port);
}
furi_hal_power_disable_otg();
furi_hal_gpio_disable_int_callback(&gpio_ext_pa7);
furi_hal_gpio_remove_int_callback(&gpio_ext_pa7);
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
furi_message_queue_free(event_queue);
delete_mutex(&state_mutex);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_timer_free(timer);
furi_record_close(RECORD_GUI);
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

View File

@@ -46,11 +46,25 @@ typedef struct {
#define KEY_WIDTH 9
#define KEY_HEIGHT 12
#define KEY_PADDING 1
#define ROW_COUNT 6
#define ROW_COUNT 7
#define COLUMN_COUNT 12
// 0 width items are not drawn, but there value is used
const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
{
{.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1},
{.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2},
{.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3},
{.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4},
{.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5},
{.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6},
{.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7},
{.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8},
{.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9},
{.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10},
{.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11},
{.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12},
},
{
{.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
{.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
@@ -224,7 +238,12 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
canvas_set_font(canvas, FontKeyboard);
// Start shifting the all keys up if on the next row (Scrolling)
uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
uint8_t initY = model->y == 0 ? 0 : 1;
if(model->y > 5) {
initY = model->y - 4;
}
for(uint8_t y = initY; y < ROW_COUNT; y++) {
const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];
uint8_t x = 0;
@@ -365,7 +384,10 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
with_view_model(
hid_keyboard->view,
HidKeyboardModel * model,
{ model->transport = bt_hid->transport; },
{
model->transport = bt_hid->transport;
model->y = 1;
},
true);
return hid_keyboard;

View File

@@ -291,7 +291,7 @@ static bool mj_process_ducky_line(
strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 2, dk.hid, plugin_state);
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state);
return true;
} else if(
strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 ||

View File

@@ -6,6 +6,7 @@
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <math.h>
#include <m-array.h>
#define TAG "MusicPlayerWorker"

View File

@@ -2,6 +2,7 @@ App(
appid="NFC_Magic",
name="NFC Magic",
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="nfc_magic_app",
requires=[
"storage",

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -0,0 +1,13 @@
App(
appid="NightstandClock",
name="Nightstand Clock",
apptype=FlipperAppType.EXTERNAL,
entry_point="clock_app",
requires=["gui"],
icon="A_Clock_14",
stack_size=2 * 1024,
fap_icon="ClockIcon.png",
fap_category="Misc",
order=81,
)

View File

@@ -0,0 +1,338 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <notification/notification_messages.h>
#include <notification/notification_app.h>
#include "clock_app.h"
/*
This is a modified version of the default clock app intended for use overnight
Up / Down control the displays brightness. Down at brightness 0 turns the notification LED on and off.
*/
int brightness = 5;
bool led = false;
NotificationApp* notif = 0;
const NotificationMessage message_red_dim = {
.type = NotificationMessageTypeLedRed,
.data.led.value = 0xFF / 16,
};
const NotificationMessage message_red_off = {
.type = NotificationMessageTypeLedRed,
.data.led.value = 0x00,
};
static const NotificationSequence led_on = {
&message_red_dim,
&message_do_not_reset,
NULL,
};
static const NotificationSequence led_off = {
&message_red_off,
&message_do_not_reset,
NULL,
};
static const NotificationSequence led_reset = {
&message_red_0,
NULL,
};
void set_backlight_brightness(float brightness) {
notif->settings.display_brightness = brightness;
notification_message(notif, &sequence_display_backlight_on);
}
void handle_up() {
if(brightness < 100) {
led = false;
notification_message(notif, &led_off);
brightness += 5;
}
set_backlight_brightness((float)(brightness / 100.f));
}
void handle_down() {
if(brightness > 0) {
brightness -= 5;
if(brightness == 0) { //trigger only on the first brightness 5 -> 0 transition
led = true;
notification_message(notif, &led_on);
}
} else if(brightness == 0) { //trigger on every down press afterwards
led = !led;
if(led) {
notification_message(notif, &led_on);
} else {
notification_message(notif, &led_off);
}
}
set_backlight_brightness((float)(brightness / 100.f));
}
static void clock_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 clock_render_callback(Canvas* const canvas, void* ctx) {
//canvas_clear(canvas);
//canvas_set_color(canvas, ColorBlack);
//avoids a bug with the brightness being reverted after the backlight-off period
set_backlight_brightness((float)(brightness / 100.f));
ClockState* state = ctx;
if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) {
//FURI_LOG_D(TAG, "Can't obtain mutex, requeue render");
PluginEvent event = {.type = EventTypeTick};
furi_message_queue_put(state->event_queue, &event, 0);
return;
}
FuriHalRtcDateTime curr_dt;
furi_hal_rtc_get_datetime(&curr_dt);
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
char time_string[TIME_LEN];
char date_string[DATE_LEN];
char meridian_string[MERIDIAN_LEN];
char timer_string[20];
if(state->time_format == LocaleTimeFormat24h) {
snprintf(
time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second);
} else {
bool pm = curr_dt.hour > 12;
bool pm12 = curr_dt.hour >= 12;
snprintf(
time_string,
TIME_LEN,
CLOCK_TIME_FORMAT,
pm ? curr_dt.hour - 12 : curr_dt.hour,
curr_dt.minute,
curr_dt.second);
snprintf(
meridian_string,
MERIDIAN_LEN,
MERIDIAN_FORMAT,
pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM);
}
if(state->date_format == LocaleDateFormatYMD) {
snprintf(
date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day);
} else if(state->date_format == LocaleDateFormatMDY) {
snprintf(
date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year);
} else {
snprintf(
date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year);
}
bool timer_running = state->timer_running;
uint32_t timer_start_timestamp = state->timer_start_timestamp;
uint32_t timer_stopped_seconds = state->timer_stopped_seconds;
furi_mutex_release(state->mutex);
canvas_set_font(canvas, FontBigNumbers);
if(timer_start_timestamp != 0) {
int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) :
timer_stopped_seconds;
snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60);
canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME
canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE
elements_button_left(canvas, "Reset");
} else {
canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, time_string);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 65, 17, AlignCenter, AlignCenter, date_string);
if(state->time_format == LocaleTimeFormat12h)
canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignCenter, meridian_string);
}
if(timer_running) {
elements_button_center(canvas, "Stop");
} else if(timer_start_timestamp != 0 && !timer_running) {
elements_button_center(canvas, "Start");
}
}
static void clock_state_init(ClockState* const state) {
state->time_format = locale_get_time_format();
state->date_format = locale_get_date_format();
//FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h");
//FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322");
//furi_hal_rtc_get_datetime(&state->datetime);
}
// Runs every 1000ms by default
static void clock_tick(void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
PluginEvent event = {.type = EventTypeTick};
// It's OK to loose this event if system overloaded
furi_message_queue_put(event_queue, &event, 0);
}
void timer_start_stop(ClockState* plugin_state) {
// START/STOP TIMER
FuriHalRtcDateTime curr_dt;
furi_hal_rtc_get_datetime(&curr_dt);
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
if(plugin_state->timer_running) {
// Update stopped seconds
plugin_state->timer_stopped_seconds = curr_ts - plugin_state->timer_start_timestamp;
} else {
if(plugin_state->timer_start_timestamp == 0) {
// Set starting timestamp if this is first time
plugin_state->timer_start_timestamp = curr_ts;
} else {
// Timer was already running, need to slightly readjust so we don't
// count the intervening time
plugin_state->timer_start_timestamp = curr_ts - plugin_state->timer_stopped_seconds;
}
}
plugin_state->timer_running = !plugin_state->timer_running;
}
void timer_reset_seconds(ClockState* plugin_state) {
if(plugin_state->timer_start_timestamp != 0) {
// Reset seconds
plugin_state->timer_running = false;
plugin_state->timer_start_timestamp = 0;
plugin_state->timer_stopped_seconds = 0;
}
}
int32_t clock_app(void* p) {
UNUSED(p);
ClockState* plugin_state = malloc(sizeof(ClockState));
plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
if(plugin_state->event_queue == NULL) {
FURI_LOG_E(TAG, "Cannot create event queue");
free(plugin_state);
return 255;
}
//FURI_LOG_D(TAG, "Event queue created");
plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(plugin_state->mutex == NULL) {
FURI_LOG_E(TAG, "Cannot create mutex");
furi_message_queue_free(plugin_state->event_queue);
free(plugin_state);
return 255;
}
//FURI_LOG_D(TAG, "Mutex created");
clock_state_init(plugin_state);
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, clock_render_callback, plugin_state);
view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue);
FuriTimer* timer =
furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue);
if(timer == NULL) {
FURI_LOG_E(TAG, "Cannot create timer");
furi_mutex_free(plugin_state->mutex);
furi_message_queue_free(plugin_state->event_queue);
free(plugin_state);
return 255;
}
//FURI_LOG_D(TAG, "Timer created");
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
furi_timer_start(timer, furi_kernel_get_tick_frequency());
//FURI_LOG_D(TAG, "Timer started");
notif = furi_record_open(RECORD_NOTIFICATION);
float tmpBrightness = notif->settings.display_brightness;
notification_message(notif, &sequence_display_backlight_enforce_on);
notification_message(notif, &led_off);
// Main loop
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100);
if(event_status != FuriStatusOk) continue;
if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue;
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeLong) {
switch(event.input.key) {
case InputKeyLeft:
// Reset seconds
timer_reset_seconds(plugin_state);
break;
case InputKeyOk:
// Toggle timer
timer_start_stop(plugin_state);
break;
case InputKeyBack:
// Exit the plugin
processing = false;
break;
default:
break;
}
} else if(event.input.type == InputTypeShort) {
switch(event.input.key) {
case InputKeyUp:
handle_up();
break;
case InputKeyDown:
handle_down();
break;
default:
break;
}
}
} /*else if(event.type == EventTypeTick) {
furi_hal_rtc_get_datetime(&plugin_state->datetime);
}*/
view_port_update(view_port);
furi_mutex_release(plugin_state->mutex);
}
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(plugin_state->event_queue);
furi_mutex_free(plugin_state->mutex);
free(plugin_state);
set_backlight_brightness(tmpBrightness);
notification_message(notif, &sequence_display_backlight_enforce_auto);
notification_message(notif, &led_reset);
return 0;
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <input/input.h>
#include <locale/locale.h>
#define TAG "Clock"
#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d"
#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d"
#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d"
#define MERIDIAN_FORMAT "%s"
#define MERIDIAN_STRING_AM "AM"
#define MERIDIAN_STRING_PM "PM"
#define TIME_LEN 12
#define DATE_LEN 14
#define MERIDIAN_LEN 3
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} PluginEvent;
typedef struct {
LocaleDateFormat date_format;
LocaleTimeFormat time_format;
FuriHalRtcDateTime datetime;
FuriMutex* mutex;
FuriMessageQueue* event_queue;
uint32_t timer_start_timestamp;
uint32_t timer_stopped_seconds;
bool timer_running;
} ClockState;

View File

@@ -2,6 +2,7 @@ App(
appid="Picopass",
name="PicoPass Reader",
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="picopass_app",
requires=[
"storage",

View File

@@ -0,0 +1,12 @@
App(
appid="Pomodoro2",
name="Pomodoro 2",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipp_pomodoro_app",
requires=["gui", "notification", "dolphin"],
stack_size=1 * 1024,
fap_category="Tools",
fap_icon_assets="images",
fap_icon="flipp_pomodoro_10.png",
fap_icon_assets_symbol="flipp_pomodoro",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View File

@@ -0,0 +1,101 @@
#include "flipp_pomodoro_app_i.h"
enum {
CustomEventConsumed = true,
CustomEventNotConsumed = false,
};
static bool flipp_pomodoro_app_back_event_callback(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
return scene_manager_handle_back_event(app->scene_manager);
};
static void flipp_pomodoro_app_tick_event_callback(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick);
};
static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
switch(event) {
case FlippPomodoroAppCustomEventStageSkip:
flipp_pomodoro__toggle_stage(app->state);
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
return CustomEventConsumed;
case FlippPomodoroAppCustomEventStageComplete:
if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) {
// REGISTER a deed on work stage complete to get an acheivement
DOLPHIN_DEED(DolphinDeedPluginGameWin);
};
flipp_pomodoro__toggle_stage(app->state);
notification_message(
app->notification_app,
stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]);
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
return CustomEventConsumed;
default:
break;
}
return scene_manager_handle_custom_event(app->scene_manager, event);
};
FlippPomodoroApp* flipp_pomodoro_app_alloc() {
FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp));
app->state = flipp_pomodoro__new();
app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app);
app->gui = furi_record_open(RECORD_GUI);
app->notification_app = furi_record_open(RECORD_NOTIFICATION);
app->view_dispatcher = view_dispatcher_alloc();
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, flipp_pomodoro_app_custom_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, flipp_pomodoro_app_back_event_callback);
app->timer_view = flipp_pomodoro_view_timer_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
FlippPomodoroAppViewTimer,
flipp_pomodoro_view_timer_get_view(app->timer_view));
scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer);
return app;
};
void flipp_pomodoro_app_free(FlippPomodoroApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
flipp_pomodoro_view_timer_free(app->timer_view);
flipp_pomodoro__destroy(app->state);
free(app);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
};
int32_t flipp_pomodoro_app(void* p) {
UNUSED(p);
FlippPomodoroApp* app = flipp_pomodoro_app_alloc();
view_dispatcher_run(app->view_dispatcher);
flipp_pomodoro_app_free(app);
return 0;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <notification/notification_messages.h>
#include "views/flipp_pomodoro_timer_view.h"
#include "modules/flipp_pomodoro.h"
typedef enum {
// Reserve first 100 events for button types and indexes, starting from 0
FlippPomodoroAppCustomEventStageSkip = 100,
FlippPomodoroAppCustomEventStageComplete, // By Expiration
FlippPomodoroAppCustomEventTimerTick,
FlippPomodoroAppCustomEventStateUpdated,
} FlippPomodoroAppCustomEvent;
typedef struct {
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notification_app;
FlippPomodoroTimerView* timer_view;
FlippPomodoroState* state;
} FlippPomodoroApp;
typedef enum {
FlippPomodoroAppViewTimer,
} FlippPomodoroAppView;

View File

@@ -0,0 +1,31 @@
#pragma once
#define FURI_DEBUG 1
/**
* Index of dependencies for the main app
*/
// Platform Imports
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view_stack.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/elements.h>
#include <dolphin/dolphin.h>
#include <input/input.h>
// App resource imports
#include "helpers/time.h"
#include "helpers/notifications.h"
#include "modules/flipp_pomodoro.h"
#include "flipp_pomodoro_app.h"
#include "scenes/flipp_pomodoro_scene.h"
#include "views/flipp_pomodoro_timer_view.h"
// Auto-compiled icons
#include "flipp_pomodoro_icons.h"

View File

@@ -0,0 +1,5 @@
#pragma once
#include <furi.h>
#define TAG "FlippPomodoro"

View File

@@ -0,0 +1,49 @@
#include <notification/notification_messages.h>
const NotificationSequence work_start_notification = {
&message_display_backlight_on,
&message_vibro_on,
&message_note_b5,
&message_delay_250,
&message_note_d5,
&message_delay_250,
&message_sound_off,
&message_vibro_off,
&message_green_255,
&message_delay_1000,
&message_green_0,
&message_delay_250,
&message_green_255,
&message_delay_1000,
NULL,
};
const NotificationSequence rest_start_notification = {
&message_display_backlight_on,
&message_vibro_on,
&message_note_d5,
&message_delay_250,
&message_note_b5,
&message_delay_250,
&message_sound_off,
&message_vibro_off,
&message_red_255,
&message_delay_1000,
&message_red_0,
&message_delay_250,
&message_red_255,
&message_delay_1000,
NULL,
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "../modules/flipp_pomodoro.h"
#include <notification/notification_messages.h>
extern const NotificationSequence work_start_notification;
extern const NotificationSequence rest_start_notification;
/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage.
const NotificationSequence* stage_start_notification_sequence_map[] = {
[FlippPomodoroStageFocus] = &work_start_notification,
[FlippPomodoroStageRest] = &rest_start_notification,
[FlippPomodoroStageLongBreak] = &rest_start_notification,
};

View File

@@ -0,0 +1,20 @@
#include <furi.h>
#include <furi_hal.h>
#include "time.h"
const int TIME_SECONDS_IN_MINUTE = 60;
const int TIME_MINUTES_IN_HOUR = 60;
uint32_t time_now() {
return furi_hal_rtc_get_timestamp();
};
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) {
const uint32_t duration_seconds = end - begin;
uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR;
uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE;
return (
TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds};
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
extern const int TIME_SECONDS_IN_MINUTE;
extern const int TIME_MINUTES_IN_HOUR;
/// @brief Container for a time period
typedef struct {
uint8_t seconds;
uint8_t minutes;
uint32_t total_seconds;
} TimeDifference;
/// @brief Time by the moment of calling
/// @return A timestamp(seconds percision)
uint32_t time_now();
/// @brief Calculates difference between two provided timestamps
/// @param begin - start timestamp of the period
/// @param end - end timestamp of the period to measure
/// @return TimeDifference struct
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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