system-prompts/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder.txt
2023-11-28 11:36:31 +08:00

1453 lines
40 KiB
Text

Example of flipper zero app source code :
- This is counter increamnt by InputKeyDown and InputKeyUp in flipper zero :
#include <furi.h>
#include <gui/gui.h>
#include <stdlib.h>
#include <gui/elements.h>
#include <input/input.h>
#include <stdio.h>
typedef struct {
int number;
bool confirm;
} AppState;
// Render callback
static void render_callback(Canvas* canvas, void* context) {
AppState* app_state = (AppState*)context;
// Clear the canvas
canvas_clear(canvas);
// Set font and draw text
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "BMI Calculator");
char numStr[12];
snprintf(numStr, sizeof(numStr), "%d", app_state->number);
elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignTop, numStr);
}
// Input callback
static void input_callback(InputEvent* input_event, void* context) {
AppState* app_state = (AppState*)context;
if(input_event->type == InputTypeShort) {
switch(input_event->key) {
case InputKeyUp:
app_state->number++;
break;
case InputKeyDown:
app_state->number--;
break;
case InputKeyOk:
app_state->confirm = true;
break;
case InputKeyBack:
// Add any action if required when back button is pressed
break;
default:
break;
}
}
}
int32_t flippertools_app(void) {
AppState app_state;
app_state.number = 0;
app_state.confirm = false;
// Create and open a viewport
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &app_state);
// Setup GUI
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// Create an input queue
FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
view_port_input_callback_set(view_port, input_callback, &app_state);
// Main loop
while(!app_state.confirm) {
view_port_update(view_port);
furi_delay_ms(100);
// Check for input events
InputEvent input_event;
while(furi_message_queue_get(input_queue, &input_event, 0) == FuriStatusOk) {
input_callback(&input_event, &app_state);
}
}
// Cleanup
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(input_queue);
return 0;
}
- This Flipper application lets you view all information's regarding your flippers experience and butthurt.
Features:
General XP and Butthurt View
Daily XP View and descriptions to each category.
Daily Butthurt View and descriptions.
Backup View to create and load backups of your flippers experience and butthurt.
full code : doc.c file :
#include "doc_i.h"
#include "helpers/doc_storage_helper.h"
#include <dialogs/dialogs.h>
#include <scenes/doc_scene.h>
bool doc_custom_event_callback(void* ctx, uint32_t event) {
furi_assert(ctx);
Doc* app = ctx;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
bool doc_back_event_callback(void* ctx) {
furi_assert(ctx);
Doc* app = ctx;
return scene_manager_handle_back_event(app->scene_manager);
}
/*
* The Dolphin service only saves 30s after the latest change,
* it can take up to that amount to get the real current values.
* The timer gets called after 10s, for 3 times and then stops.
*/
void doc_dolphin_timer_callback(void* ctx) {
furi_assert(ctx);
Doc* app = ctx;
app->dolphin_timer_counter++;
if(app->dolphin_timer_counter <= 3) {
FURI_LOG_D(TAG, "Loading new state after %hhus", app->dolphin_timer_counter * 10);
doc_dolphin_state_load(app->dolphin);
furi_delay_ms(20);
if(app->in_selection) {
doc_selection_request_redraw(app->selection);
}
if(app->in_description) {
doc_description_request_redraw(app->description);
}
}
if(app->dolphin_timer_counter == 3) {
FURI_LOG_D(TAG, "30s reached, stopping timer.");
furi_timer_stop(app->dolphin_timer);
}
}
Doc* doc_alloc() {
Doc* app = malloc(sizeof(Doc));
app->dolphin = malloc(sizeof(DolphinState));
app->dolphin_timer = furi_timer_alloc(doc_dolphin_timer_callback, FuriTimerTypePeriodic, app);
app->file_path = furi_string_alloc();
//? ------------- Records -------------
app->gui = furi_record_open(RECORD_GUI);
app->dialogs = furi_record_open(RECORD_DIALOGS);
//? ----------- Records End -----------
// ViewDispatcher & SceneManager
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&doc_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, doc_custom_event_callback);
view_dispatcher_set_navigation_event_callback(app->view_dispatcher, doc_back_event_callback);
//! -------------- DEBUG --------------
app->notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(app->notification, &sequence_display_backlight_on);
//! ------------ DEBUG END ------------
//? -------------- Views --------------
app->selection = doc_selection_alloc();
view_dispatcher_add_view(app->view_dispatcher, DocSelectionView, doc_selection_get_view(app->selection));
app->description = doc_description_alloc();
view_dispatcher_add_view(app->view_dispatcher, DocDescriptionView, doc_description_get_view(app->description));
app->text_input = text_input_alloc();
view_dispatcher_add_view(app->view_dispatcher, DocTextInputView, text_input_get_view(app->text_input));
//? ------------ Views End ------------
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
return app;
}
void doc_free(Doc* app) {
//! -------------- DEBUG --------------
furi_record_close(RECORD_NOTIFICATION);
app->notification = NULL;
//! ------------ DEBUG END ------------
//? -------------- Views --------------
view_dispatcher_remove_view(app->view_dispatcher, DocSelectionView);
doc_selection_free(app->selection);
view_dispatcher_remove_view(app->view_dispatcher, DocDescriptionView);
doc_description_free(app->description);
view_dispatcher_remove_view(app->view_dispatcher, DocTextInputView);
text_input_free(app->text_input);
//? ------------ Views End ------------
// ViewDispatcher & SceneManager
scene_manager_free(app->scene_manager);
view_dispatcher_free(app->view_dispatcher);
//? ------------- Records -------------
furi_record_close(RECORD_GUI);
app->gui = NULL;
furi_record_close(RECORD_DIALOGS);
app->dialogs = NULL;
//? ----------- Records End -----------
furi_string_free(app->file_path);
furi_timer_free(app->dolphin_timer);
free(app->dolphin);
free(app);
}
uint32_t doc_app(void* p) {
UNUSED(p);
Doc* app = doc_alloc();
doc_dolphin_state_load(app->dolphin);
furi_timer_start(app->dolphin_timer, 10 * 1000);
scene_manager_next_scene(app->scene_manager, DocSceneMenu);
view_dispatcher_run(app->view_dispatcher);
doc_free(app);
return 0;
}
doc_i.h file :
#pragma once
#include <helpers/dolphin_state.h>
//? -------------- Views --------------
#include <views/doc_selection.h>
#include <views/doc_description.h>
#include <gui/modules/text_input.h>
//? ------------ Views End ------------
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <dialogs/dialogs.h>
//! -------------- DEBUG --------------
#include <notification/notification_messages.h>
//! ------------ DEBUG END ------------
#define TAG "FlipperDoc"
typedef struct {
DolphinState* dolphin;
FuriTimer* dolphin_timer;
uint8_t dolphin_timer_counter;
//? -------------- Views --------------
DocSelection* selection;
bool in_selection;
DocDescription* description;
bool in_description;
TextInput* text_input;
//? ------------ Views End ------------
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
DialogsApp* dialogs;
FuriString* file_path;
char text_input_array[50];
//! -------------- DEBUG --------------
NotificationApp* notification;
//! ------------ DEBUG END ------------
} Doc;
enum {
DocSelectionView,
DocDescriptionView,
DocTextInputView,
};
doc_icons.h file :
#pragma once
#include <gui/icon.h>
extern const Icon I_flipperdoc;
extern const Icon I_doc_smallscreen_light;
extern const Icon I_doc_bigscreen_light;
extern const Icon I_doc_button_left_small;
extern const Icon I_doc_button_up;
extern const Icon I_doc_button_down;
application.fam :
# For details & more options, see documentation/AppManifests.md in firmware repo
App(
appid="flipperdoc",
name="Flipper Doctor",
apptype=FlipperAppType.EXTERNAL,
entry_point="doc_app",
stack_size=1 * 1024,
fap_category="Tools",
fap_version="0.1",
fap_icon="images/flipperdoc.png",
fap_author="JulanDeAlb",
fap_weburl="https://github.com/julandealb/flipperdoc",
fap_icon_assets="images",
)
views/doc_description.c :
#include "doc_description.h"
#include "doc_view_common.h"
#include <doc_icons.h>
struct DocDescription {
View* view;
DocDescriptionCallback callback;
void* ctx;
};
typedef struct {
FuriString* text;
uint8_t category;
//Internal
uint8_t size;
uint8_t index;
} DocDescriptionViewModel;
static void doc_description_draw_callback(Canvas* canvas, void* ctx) {
furi_assert(ctx);
DocDescriptionViewModel* vm = ctx;
canvas_draw_icon(canvas, 0, 0, &I_doc_bigscreen_light);
// Scrolling Arrow
if(vm->index > 0) {
canvas_draw_icon(canvas, 113, 13, &I_doc_button_up);
}
if(vm->size > 4 &&
vm->index < vm->size - 4) {
canvas_draw_icon(canvas, 113, 39, &I_doc_button_down);
}
// Text
doc_draw_text(canvas, vm->text, 18, 110, 16, 10, vm->index, 4);
}
static bool doc_description_input_callback(InputEvent* event, void* ctx) {
furi_assert(ctx);
DocDescription* instance = ctx;
bool consumed = false;
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
switch(event->key) {
case InputKeyUp:
with_view_model(
instance->view,
DocDescriptionViewModel* model,
{
if(model->index > 0) {
model->index--;
}
},
true);
consumed = true;
break;
case InputKeyDown:
with_view_model(
instance->view,
DocDescriptionViewModel* model,
{
if(model->index < model->size - 4) {
model->index++;
}
},
true);
consumed = true;
break;
case InputKeyBack:
with_view_model(
instance->view,
DocDescriptionViewModel* model,
{
model->index = 0;
},
false);
break;
default:
break;
}
}
return consumed;
}
//? Basic Functions
View* doc_description_get_view(DocDescription* instance) {
furi_assert(instance);
return instance->view;
}
void doc_description_set_callback(DocDescription* instance, DocDescriptionCallback callback, void* ctx) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->ctx = ctx;
}
DocDescription* doc_description_alloc() {
DocDescription* instance = malloc(sizeof(DocDescription));
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DocDescriptionViewModel));
view_set_draw_callback(instance->view, doc_description_draw_callback);
view_set_input_callback(instance->view, doc_description_input_callback);
view_set_context(instance->view, instance);
with_view_model(
instance->view,
DocDescriptionViewModel* vm,
{
vm->text = furi_string_alloc();
}, false);
return instance;
}
void doc_description_free(DocDescription* instance) {
furi_assert(instance);
with_view_model(
instance->view,
DocDescriptionViewModel* vm,
{
furi_string_free(vm->text);
}, false);
view_free(instance->view);
free(instance);
}
//? Basic Functions End
//? Custom Functions
FuriString* doc_description_get_string(DocDescription* instance) {
furi_assert(instance);
FuriString* text;
with_view_model(
instance->view,
DocDescriptionViewModel* vm,
{
text = vm->text;
}, true);
return text;
}
uint8_t doc_description_get_category(DocDescription* instance) {
furi_assert(instance);
uint8_t category;
with_view_model(
instance->view,
DocDescriptionViewModel* vm,
{
category = vm->category;
}, false);
return category;
}
void doc_description_set_category(DocDescription* instance, uint8_t category) {
furi_assert(instance);
with_view_model(
instance->view,
DocDescriptionViewModel* vm,
{
vm->category = category;
}, true);
}
void doc_description_set_size(DocDescription* instance, uint8_t size) {
furi_assert(instance);
with_view_model(
instance->view,
DocDescriptionViewModel* vm,
{
vm->size = size;
}, true);
}
void doc_description_request_redraw(DocDescription* instance) {
furi_assert(instance);
if(instance->callback != NULL) {
instance->callback(instance->ctx, (uint8_t) - 1);
}
}
void doc_description_force_redraw(DocDescription* instance) {
furi_assert(instance);
with_view_model(instance->view, DocDescriptionViewModel* vm, { UNUSED(vm); }, true);
}
//? Custom Functions End
views/doc_description.h :
#pragma once
#include <gui/view.h>
typedef struct DocDescription DocDescription;
typedef void (*DocDescriptionCallback)(void* ctx, uint8_t index);
//? Basic Functions
View* doc_description_get_view(DocDescription* instance);
void doc_description_set_callback(DocDescription* instance, DocDescriptionCallback callback, void* ctx);
DocDescription* doc_description_alloc();
void doc_description_free(DocDescription* instance);
//? Basic Functions End
//? Custom Functions
FuriString* doc_description_get_string(DocDescription* instance);
uint8_t doc_description_get_category(DocDescription* instance);
void doc_description_set_category(DocDescription* instance, uint8_t category);
void doc_description_set_size(DocDescription* instance, uint8_t size);
void doc_description_request_redraw(DocDescription* instance);
void doc_description_force_redraw(DocDescription* instance);
//? Custom Functions End
views/doc_selection.c :
#include "doc_selection.h"
#include "doc_view_common.h"
#include <doc_icons.h>
struct DocSelection {
View* view;
DocSelectionCallback callback;
void* ctx;
};
typedef struct {
const char* title;
FuriString* text;
const char* footer;
uint8_t category;
//Internal
uint8_t size;
uint8_t position;
uint8_t window_position;
} DocSelectionViewModel;
static void doc_selection_draw_callback(Canvas* canvas, void* ctx) {
furi_assert(ctx);
DocSelectionViewModel* vm = ctx;
canvas_draw_icon(canvas, 0, 0, &I_doc_smallscreen_light);
// Selection Arrow
uint8_t selection_index = vm->position - vm->window_position;
if(selection_index == 0) {
canvas_draw_icon(canvas, 123, 16, &I_doc_button_left_small);
} else if(selection_index == 1) {
canvas_draw_icon(canvas, 123, 25, &I_doc_button_left_small);
} else {
canvas_draw_icon(canvas, 123, 34, &I_doc_button_left_small);
}
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 85, 11, AlignCenter, AlignBottom, vm->title);
canvas_set_font(canvas, FontSecondary);
// Text
doc_draw_text(canvas, vm->text, 44, 121, 22, 9, vm->window_position, 3);
// Footer
canvas_draw_str_aligned(canvas, 87, 54, AlignCenter, AlignBottom, vm->footer);
}
static bool doc_selection_input_callback(InputEvent* event, void* ctx) {
furi_assert(ctx);
DocSelection* instance = ctx;
bool consumed = false;
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
switch(event->key) {
case InputKeyUp:
with_view_model(
instance->view,
DocSelectionViewModel* model,
{
if(model->position > 0) {
model->position--;
if(model->position == model->window_position && model->window_position > 0) {
model->window_position--;
}
} else {
model->position = model->size - 1;
if(model->position > 2) {
model->window_position = model->position - 2;
}
}
},
true);
consumed = true;
break;
case InputKeyDown:
with_view_model(
instance->view,
DocSelectionViewModel* model,
{
if(model->position < model->size - 1) {
model->position++;
if(model->position - model->window_position > 1 && model->window_position < model->size - 3) {
model->window_position++;
}
} else {
model->position = 0;
model->window_position = 0;
}
},
true);
consumed = true;
break;
case InputKeyOk:
with_view_model(
instance->view,
DocSelectionViewModel* model,
{
if(instance->callback) {
instance->callback(instance->ctx, model->position);
}
},
false);
break;
default:
break;
}
}
return consumed;
}
//? Basic Functions
View* doc_selection_get_view(DocSelection* instance) {
furi_assert(instance);
return instance->view;
}
void doc_selection_set_callback(DocSelection* instance, DocSelectionCallback callback, void* ctx) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->ctx = ctx;
}
DocSelection* doc_selection_alloc() {
DocSelection* instance = malloc(sizeof(DocSelection));
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DocSelectionViewModel));
view_set_draw_callback(instance->view, doc_selection_draw_callback);
view_set_input_callback(instance->view, doc_selection_input_callback);
view_set_context(instance->view, instance);
with_view_model(
instance->view,
DocSelectionViewModel* vm,
{
vm->text = furi_string_alloc();
}, false);
return instance;
}
void doc_selection_free(DocSelection* instance) {
furi_assert(instance);
with_view_model(
instance->view,
DocSelectionViewModel* vm,
{
furi_string_free(vm->text);
}, false);
view_free(instance->view);
free(instance);
}
//? Basic Functions End
//? Custom Functions
void doc_selection_set_title(DocSelection* instance, const char* title) {
furi_assert(instance);
with_view_model(
instance->view,
DocSelectionViewModel* vm,
{
vm->title = title;
}, true);
}
FuriString* doc_selection_get_string(DocSelection* instance) {
furi_assert(instance);
FuriString* text;
with_view_model(
instance->view,
DocSelectionViewModel* vm,
{
text = vm->text;
}, true);
return text;
}
void doc_selection_set_footer(DocSelection* instance, const char* footer) {
furi_assert(instance);
with_view_model(
instance->view,
DocSelectionViewModel* vm,
{
vm->footer = footer;
}, true);
}
void doc_selection_set_index(DocSelection* instance, uint8_t index) {
furi_assert(instance);
with_view_model(
instance->view,
DocSelectionViewModel* model,
{
uint8_t position = index;
if(position >= model->size) {
position = 0;
}
model->position = position;
model->window_position = position;
if(model->window_position > 0) {
model->window_position -= 1;
}
if(model->size <= 3) {
model->window_position = 0;
} else {
if(model->window_position >= model->size - 3) {
model->window_position = model->size - 3;
}
}
},
true);
}
void doc_selection_set_size(DocSelection* instance, uint8_t size) {
furi_assert(instance);
with_view_model(
instance->view,
DocSelectionViewModel* vm,
{
vm->size = size;
}, true);
}
void doc_selection_request_redraw(DocSelection* instance) {
furi_assert(instance);
if(instance->callback != NULL) {
instance->callback(instance->ctx, (uint8_t) - 1);
}
}
void doc_selection_force_redraw(DocSelection* instance) {
furi_assert(instance);
with_view_model(instance->view, DocSelectionViewModel* vm, { UNUSED(vm); }, true);
}
//? Custom Functions End
views/doc_view_common.c :
#include "doc_view_common.h"
void doc_draw_text(Canvas* canvas, FuriString* text,
uint8_t x_one, uint8_t x_two, uint8_t y,
uint8_t font_height, uint8_t start_index, uint8_t max_index) {
furi_assert(canvas);
FuriString* str = furi_string_alloc();
const char* start = furi_string_get_cstr(text);
char* mid;
char* end;
uint8_t temp_index = 0;
do {
mid = strchr(start, '\t');
end = strchr(start, '\n');
if(mid && end && 0 < end - mid) {
furi_string_set_strn(str, start, mid - start);
if(temp_index >= start_index) {
canvas_draw_str_aligned(canvas, x_one, y, AlignLeft, AlignBottom, furi_string_get_cstr(str));
}
}
if(end && mid) {
furi_string_set_strn(str, mid, end - start);
start = end + 1;
} else if(end) {
furi_string_set_strn(str, start, end - start);
start = end + 1;
} else {
furi_string_set(str, start);
}
if(temp_index >= start_index) {
canvas_draw_str_aligned(canvas, x_two, y, AlignRight, AlignBottom, furi_string_get_cstr(str));
y += font_height;
}
temp_index++;
} while(end && y < 64 && temp_index <= start_index + max_index - 1);
furi_string_free(str);
}
views/doc_view_common.h :
#pragma once
#include <gui/view.h>
void doc_draw_text(Canvas* canvas, FuriString* text,
uint8_t x_one, uint8_t x_two, uint8_t y,
uint8_t font_height, uint8_t start_index, uint8_t max_index);
this app will generate a password in flipper zero , source code :
#include <furi.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <stdlib.h>
#include <passgen_icons.h>
#define PASSGEN_MAX_LENGTH 16
#define PASSGEN_CHARACTERS_LENGTH (26*4)
#define PASSGEN_DIGITS "0123456789"
#define PASSGEN_LETTERS_LOW "abcdefghijklmnopqrstuvwxyz"
#define PASSGEN_LETTERS_UP "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define PASSGEN_SPECIAL "!#$%^&*.-_"
typedef enum PassGen_Alphabet
{
Digits = 1,
Lowercase = 2,
Uppercase = 4,
Special = 8,
DigitsLower = Digits | Lowercase,
DigitsAllLetters = Digits | Lowercase | Uppercase,
Mixed = DigitsAllLetters | Special
} PassGen_Alphabet;
const int AlphabetLevels[] = { Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed };
const char* AlphabetLevelNames[] = { "1234", "abcd", "ab12", "Ab12", "Ab1#" };
const int AlphabetLevelsCount = sizeof(AlphabetLevels) / sizeof(int);
const NotificationSequence PassGen_Alert_vibro = {
&message_vibro_on,
&message_blue_255,
&message_delay_50,
&message_vibro_off,
NULL,
};
typedef struct {
FuriMessageQueue* input_queue;
ViewPort* view_port;
Gui* gui;
FuriMutex** mutex;
NotificationApp* notify;
char password[PASSGEN_MAX_LENGTH+1];
char alphabet[PASSGEN_CHARACTERS_LENGTH+1];
int length;
int level;
} PassGen;
void state_free(PassGen* app) {
gui_remove_view_port(app->gui, app->view_port);
furi_record_close(RECORD_GUI);
view_port_free(app->view_port);
furi_message_queue_free(app->input_queue);
furi_mutex_free(app->mutex);
furi_record_close(RECORD_NOTIFICATION);
free(app);
}
static void input_callback(InputEvent* input_event, void* ctx) {
PassGen* app = ctx;
if(input_event->type == InputTypeShort) {
furi_message_queue_put(app->input_queue, input_event, 0);
}
}
static void render_callback(Canvas* canvas, void* ctx) {
char str_length[8];
PassGen* app = ctx;
furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk);
canvas_clear(canvas);
canvas_draw_box(canvas, 0, 0, 128, 14);
canvas_set_color(canvas, ColorWhite);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 11, "Password Generator");
canvas_set_color(canvas, ColorBlack);
canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignCenter, app->password);
// Navigation menu:
canvas_set_font(canvas, FontSecondary);
canvas_draw_icon(canvas, 96, 52, &I_Pin_back_arrow_10x8);
canvas_draw_str(canvas, 108, 60, "Exit");
canvas_draw_icon(canvas, 54, 52, &I_Vertical_arrow_7x9);
canvas_draw_str(canvas, 64, 60, AlphabetLevelNames[app->level]);
snprintf(str_length, sizeof(str_length), "Len: %d", app->length);
canvas_draw_icon(canvas, 4, 53, &I_Horizontal_arrow_9x7);
canvas_draw_str(canvas, 15, 60, str_length);
furi_mutex_release(app->mutex);
}
void build_alphabet(PassGen* app)
{
PassGen_Alphabet mode = AlphabetLevels[app->level];
app->alphabet[0] = '\0';
if ((mode & Digits) != 0)
strcat(app->alphabet, PASSGEN_DIGITS);
if ((mode & Lowercase) != 0)
strcat(app->alphabet, PASSGEN_LETTERS_LOW);
if ((mode & Uppercase) != 0)
strcat(app->alphabet, PASSGEN_LETTERS_UP);
if ((mode & Special) != 0)
strcat(app->alphabet, PASSGEN_SPECIAL);
}
PassGen* state_init() {
PassGen* app = malloc(sizeof(PassGen));
app->length = 8;
app->level = 2;
build_alphabet(app);
app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
app->view_port = view_port_alloc();
app->gui = furi_record_open(RECORD_GUI);
app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
view_port_input_callback_set(app->view_port, input_callback, app);
view_port_draw_callback_set(app->view_port, render_callback, app);
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
app->notify = furi_record_open(RECORD_NOTIFICATION);
return app;
}
void generate(PassGen* app)
{
int hi = strlen(app->alphabet);
for (int i=0; i<app->length; i++)
{
int x = rand() % hi;
app->password[i]=app->alphabet[x];
}
app->password[app->length] = '\0';
}
void update_password(PassGen* app, bool vibro)
{
generate(app);
if (vibro)
notification_message(app->notify, &PassGen_Alert_vibro);
else
notification_message(app->notify, &sequence_blink_blue_100);
view_port_update(app->view_port);
}
int32_t passgenapp(void) {
PassGen* app = state_init();
generate(app);
while(1) {
InputEvent input;
while(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk);
if (input.type == InputTypeShort)
{
switch (input.key) {
case InputKeyBack:
furi_mutex_release(app->mutex);
state_free(app);
return 0;
case InputKeyDown:
if (app->level > 0)
{
app->level--;
build_alphabet(app);
update_password(app, false);
}
else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyUp:
if (app->level < AlphabetLevelsCount - 1)
{
app->level++;
build_alphabet(app);
update_password(app, false);
}
else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyLeft:
if (app->length > 1)
{
app->length--;
update_password(app, false);
}
else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyRight:
if (app->length < PASSGEN_MAX_LENGTH)
{
app->length++;
update_password(app, false);
}
else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyOk:
update_password(app, true);
break;
default:
break;
}
}
furi_mutex_release(app->mutex);
}
}
state_free(app);
return 0;
}
Simple Flashlight source code for flipper zero Plugin :
// by @xMasterX
#include <furi.h>
#include <furi_hal_power.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#include <gui/elements.h>
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} PluginEvent;
typedef struct {
FuriMutex* mutex;
bool is_on;
} PluginState;
static void render_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
const PluginState* plugin_state = ctx;
furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Flashlight");
canvas_set_font(canvas, FontSecondary);
if(!plugin_state->is_on) {
elements_multiline_text_aligned(
canvas, 64, 28, AlignCenter, AlignTop, "Press OK button turn on");
} else {
elements_multiline_text_aligned(canvas, 64, 28, AlignCenter, AlignTop, "Light is on!");
elements_multiline_text_aligned(
canvas, 64, 40, AlignCenter, AlignTop, "Press OK button to off");
}
furi_mutex_release(plugin_state->mutex);
}
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 flash_toggle(PluginState* const plugin_state) {
furi_hal_gpio_write(&gpio_ext_pc3, false);
furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
if(plugin_state->is_on) {
furi_hal_gpio_write(&gpio_ext_pc3, false);
plugin_state->is_on = false;
} else {
furi_hal_gpio_write(&gpio_ext_pc3, true);
plugin_state->is_on = true;
}
}
int32_t flashlight_app() {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
PluginState* plugin_state = malloc(sizeof(PluginState));
plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!plugin_state->mutex) {
FURI_LOG_E("flashlight", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(plugin_state);
return 255;
}
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, plugin_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);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
case InputKeyUp:
case InputKeyDown:
case InputKeyRight:
case InputKeyLeft:
break;
case InputKeyOk:
flash_toggle(plugin_state);
break;
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}
}
furi_mutex_release(plugin_state->mutex);
view_port_update(view_port);
}
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(event_queue);
furi_mutex_free(plugin_state->mutex);
return 0;
}
color guess source code for flipper zero app :
#include "color_guess.h"
#include "helpers/digits.h"
bool color_guess_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
ColorGuess* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
void color_guess_tick_event_callback(void* context) {
furi_assert(context);
ColorGuess* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
//leave app if back button pressed
bool color_guess_navigation_event_callback(void* context) {
furi_assert(context);
ColorGuess* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
ColorGuess* color_guess_app_alloc() {
ColorGuess* app = malloc(sizeof(ColorGuess));
app->gui = furi_record_open(RECORD_GUI);
app->notification = furi_record_open(RECORD_NOTIFICATION);
app->error = false;
// Set Defaults if no config exists
app->haptic = 1;
app->led = 1;
app->save_settings = 1;
// Load configs
color_guess_read_settings(app);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_display_backlight_on);
//Scene additions
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
app->scene_manager = scene_manager_alloc(&color_guess_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, color_guess_navigation_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, color_guess_tick_event_callback, 100);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, color_guess_custom_event_callback);
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, ColorGuessViewIdMenu, submenu_get_view(app->submenu));
app->variable_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
ColorGuessViewIdSettings,
variable_item_list_get_view(app->variable_item_list));
app->color_guess_startscreen = color_guess_startscreen_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
ColorGuessViewIdStartscreen,
color_guess_startscreen_get_view(app->color_guess_startscreen));
app->color_guess_color_set = color_guess_color_set_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
ColorGuessViewIdColorSet,
color_guess_color_set_get_view(app->color_guess_color_set));
app->color_guess_play = color_guess_play_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
ColorGuessViewIdPlay,
color_guess_play_get_view(app->color_guess_play));
//End Scene Additions
return app;
}
void color_guess_app_free(ColorGuess* app) {
furi_assert(app);
// Scene manager
scene_manager_free(app->scene_manager);
// View Dispatcher
view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdMenu);
view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdStartscreen);
view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdColorSet);
view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdPlay);
view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdSettings);
submenu_free(app->submenu);
view_dispatcher_free(app->view_dispatcher);
// GUI
furi_record_close(RECORD_GUI);
app->view_port = NULL;
app->gui = NULL;
app->notification = NULL;
//Remove whatever is left
free(app);
}
int32_t color_guess_app(void* p) {
UNUSED(p);
ColorGuess* app = color_guess_app_alloc();
if(app->error) {
return 255;
}
if(!furi_hal_region_is_provisioned()) {
color_guess_app_free(app);
return 1;
}
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(app->scene_manager, ColorGuessSceneStartscreen);
furi_hal_power_suppress_charge_enter();
view_dispatcher_run(app->view_dispatcher);
color_guess_save_settings(app);
furi_hal_power_suppress_charge_exit();
color_guess_app_free(app);
return 0;
}
example of hello world with face draw :
helloworld.c :
#include <furi.h>
#include <gui/gui.h>
#include <stdlib.h>
#include <gui/elements.h>
// Render callback
static void render_callback(Canvas* canvas, void* context) {
// Clear the canvas
canvas_clear(canvas);
// Set font and draw "Hello World" with a twist
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Hello World!");
// Draw a smiley face under the text
canvas_draw_circle(canvas, 64, 50, 10);
canvas_draw_line(canvas, 60, 47, 62, 47); // Corrected line
canvas_draw_line(canvas, 66, 47, 68, 47); // Corrected line
}
// Input callback - exit on back button
static void input_callback(InputEvent* input_event, void* context) {
bool* running = context;
if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
*running = false;
}
}
// Main app function
int32_t hello_world_app(void) {
// Setup GUI and viewport
Gui* gui = furi_record_open(RECORD_GUI);
ViewPort* view_port = view_port_alloc();
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
view_port_draw_callback_set(view_port, render_callback, NULL);
// Main loop
bool running = true;
view_port_input_callback_set(view_port, input_callback, &running);
while(running) {
view_port_update(view_port);
furi_delay_ms(100);
}
// Cleanup
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
return 0;
}
application.fam :
App(
appid="hello_world",
name="Hello World",
apptype=FlipperAppType.EXTERNAL,
entry_point="hello_world_app",
stack_size=1 * 1024,
fap_category="Tools",
fap_version="1.0",
fap_icon="images/icon.png",
fap_author="YourName",
fap_weburl="https://yourwebsite.com"
)