diff --git a/plugins/basic/README.md b/plugins/basic/README.md new file mode 100644 index 0000000..671ec9f --- /dev/null +++ b/plugins/basic/README.md @@ -0,0 +1,63 @@ +# BASIC DEMO +## Introduction +This is a basic application for the Flipper Zero. The goal of this project is to use it as a starting point for other applications. + + +## Installation Directions +This project is intended to be overlayed on top of an existing firmware repo. +- Clone, Build & Deploy an existing flipper zero firmware repo. See this [tutorial](/firmware/updating/README.md) for updating firmware. +- Copy the "basic_demo" [folder](..) to the \applications\plugins\basic_demo folder in your firmware. +- Build & deploy the firmware. See this [tutorial](/firmware/updating/README.md) for updating firmware. +- NOTE: You can also extract the basic_demo.FAP from resources.tar file and use qFlipper to copy the file to the SD Card/apps/Misc folder. + + +## Running the updated firmware +These directions assume you are starting at the flipper desktop. If not, please press the back button until you are at the desktop. + +- Press the OK button on the flipper to pull up the main menu. +- Choose "Applications" from the menu. +- Choose "Misc" from the sub-menu. +- Choose "Basic Demo" + +- The flipper should say "Basic Demo". + +- Press the BACK button to exit. + + +## How it works +- application.fam + - specifies the name of our application. + - specifies the entry point for our application. + - specifies we use the GUI. + - specifies our icon is the basic_demo.png file. + - specifies our application can be found in the "Misc" category. + +- basic_demo.png + - The icon for our application. + +- basic_demo_app.c + - We #include the libraries we referece. + - We define DemoEventType (so we know the reason for events) + - We define DemoEvent (which has the event type and its data) used for adding to an event queue. + - We define DemoData (data used by our application) + - We define DemoContext (pointer to event queue, pointer to application data, and pointer to mutex [to safely access the data]) + - We create a basic_demo_input_callback(...) method that queues a key event. + - We create a basic_demo_render_callback(...) method that does the screen rendering. + - We acquire the mutex, so that no other thread can modify the data. + - If unsuccessful, we don't render anything this frame. + - We select the Primary font. We render the text "Basic Demo". + - We release the mutex, so other threads may modify the data. + - We create the entrypoint basic_demo_app(...) method + - We configure our initial data state + - We create a queue for events. + - We setup view_port_draw_callback_set(...) to invoke basic_demo_render_callback when rendering should happen. + - We setup view_port_input_callback_set(...) to invoke basic_demo_input_callback when button is pressed. + - We open GUI and register view_port. + - We create a message pump loop + - We get an event from the queue. + - If it is key message. + - If it is a short press of back key, we set processing=false which will exit our message loop. + - The message loop continues until processing is false. + - We release our application resources. + - We exit the program. + \ No newline at end of file diff --git a/plugins/basic/application.fam b/plugins/basic/application.fam new file mode 100644 index 0000000..dc4226a --- /dev/null +++ b/plugins/basic/application.fam @@ -0,0 +1,10 @@ +App( + appid="Basic_Demo", + name="Basic Demo", + apptype=FlipperAppType.EXTERNAL, + entry_point="basic_demo_app", + requires=["gui"], + stack_size=2 * 1024, + fap_icon="basic_demo.png", + fap_category="Misc", +) diff --git a/plugins/basic/basic_demo.png b/plugins/basic/basic_demo.png new file mode 100644 index 0000000..522b649 Binary files /dev/null and b/plugins/basic/basic_demo.png differ diff --git a/plugins/basic/basic_demo_app.c b/plugins/basic/basic_demo_app.c new file mode 100644 index 0000000..87237a4 --- /dev/null +++ b/plugins/basic/basic_demo_app.c @@ -0,0 +1,128 @@ +/* +@CodeAllNight +https://github.com/jamisonderek/flipper-zero-tutorials + +This is a basic application for the Flipper Zero. +The goal of this project is to use it as a starting point for other applications. + +*/ + +#include +#include + +#include +#include + +#include +#include + +#define TAG "basic_demo_app" + +typedef enum { + DemoEventTypeKey, + // You can add additional events here. +} DemoEventType; + +typedef struct { + DemoEventType type; // The reason for this event. + InputEvent input; // This data is specific to keypress data. + // You can add additional data that is helpful for your events. +} DemoEvent; + +typedef struct { + FuriString* buffer; + // You can add additional state here. +} DemoData; + +typedef struct { + FuriMessageQueue* queue; // Message queue (DemoEvent items to process). + FuriMutex* mutex; // Used to provide thread safe access to data. + DemoData* data; // Data accessed by multiple threads (acquire the mutex before accessing!) +} DemoContext; + +// Invoked when input (button press) is detected. We queue a message and then return to the caller. +static void basic_demo_input_callback(InputEvent* input_event, FuriMessageQueue* queue) { + furi_assert(queue); + DemoEvent event = {.type = DemoEventTypeKey, .input = *input_event}; + furi_message_queue_put(queue, &event, FuriWaitForever); +} + +// Invoked by the draw callback to render the screen. We render our UI on the callback thread. +static void basic_demo_render_callback(Canvas* canvas, void* ctx) { + // Attempt to aquire context, so we can read the data. + DemoContext* demo_context = ctx; + if(furi_mutex_acquire(demo_context->mutex, 200) != FuriStatusOk) { + return; + } + + DemoData* data = demo_context->data; + furi_string_printf(data->buffer, "Basic"); + furi_string_cat_printf(data->buffer, " demo"); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 45, 30, AlignLeft, AlignTop, furi_string_get_cstr(data->buffer)); + + // Release the context, so other threads can update the data. + furi_mutex_release(demo_context->mutex); +} + +int32_t basic_demo_app(void* p) { + UNUSED(p); + + // Configure our initial data. + DemoContext* demo_context = malloc(sizeof(DemoContext)); + demo_context->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + demo_context->data = malloc(sizeof(DemoData)); + demo_context->data->buffer = furi_string_alloc(); + + // Queue for events (tick or input) + demo_context->queue = furi_message_queue_alloc(8, sizeof(DemoEvent)); + + // Set ViewPort callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, basic_demo_render_callback, demo_context); + view_port_input_callback_set(view_port, basic_demo_input_callback, demo_context->queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Main loop + DemoEvent event; + bool processing = true; + do { + if (furi_message_queue_get(demo_context->queue, &event, FuriWaitForever) == FuriStatusOk) { + FURI_LOG_T(TAG, "Got event type: %d", event.type); + switch (event.type) { + case DemoEventTypeKey: + // Short press of back button exits the program. + if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) { + FURI_LOG_I(TAG, "Short-Back pressed. Exiting program."); + processing = false; + } + break; + default: + break; + } + + // Send signal to update the screen (callback will get invoked at some point later.) + view_port_update(view_port); + } else { + // We had an issue getting message from the queue, so exit application. + processing = false; + } + } while (processing); + + // Free resources + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close(RECORD_GUI); + furi_message_queue_free(demo_context->queue); + furi_mutex_free(demo_context->mutex); + furi_string_free(demo_context->data->buffer); + free(demo_context->data); + free(demo_context); + + return 0; +} \ No newline at end of file