1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-13 05:19:50 +04:00

FurEventLoop: add support for FuriEventFlag, simplify API (#3958)

* Core: event_flag, removing duplicate code
* event_loop: add support furi_event_flags
* Examples: add missing free in event loop examples
* Furi: fix event flag
* Sync api symbols
* Unit_test: evet_loop_event_flags
* Fix multiple waiting list elements handling
* Unit_test: add event_loop_event_flag test
* FURI: event_loop add restrictions
* Fix multiple waiting lists items for good
* Improve FuriEventLoop unit tests
* Abolish callback return value
* Remove return value from callback signature
* Use bool level value instead of int32_t
* Add unit tests for FuriStreamBuffer
* Add unit tests for FuriSemaphore
* Speed up test execution
* Improve docs
* Add a stub for furi os-level primitives
* Add more checks for edge cases
* Allow event loop notification from ISR
* Bump api version

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com>
Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com>
This commit is contained in:
Skorpionm
2024-10-31 05:58:16 +04:00
committed by GitHub
parent 8427ec0098
commit 6d823835df
27 changed files with 971 additions and 305 deletions

View File

@@ -1,3 +1,12 @@
App(
appid="example_event_loop_event_flags",
name="Example: Event Loop Event Flags",
apptype=FlipperAppType.EXTERNAL,
sources=["example_event_loop_event_flags.c"],
entry_point="example_event_loop_event_flags_app",
fap_category="Examples",
)
App(
appid="example_event_loop_timer",
name="Example: Event Loop Timer",

View File

@@ -0,0 +1,173 @@
/**
* @file example_event_loop_event_flags.c
* @brief Example application demonstrating the use of the FuriEventFlag primitive in FuriEventLoop instances.
*
* This application receives keystrokes from the input service and sets the appropriate flags,
* which are subsequently processed in the event loop
*/
#include <furi.h>
#include <gui/gui.h>
#include <gui/view_port.h>
#include <furi_hal_random.h>
#define TAG "ExampleEventLoopEventFlags"
typedef struct {
Gui* gui;
ViewPort* view_port;
FuriEventLoop* event_loop;
FuriEventFlag* event_flag;
} EventLoopEventFlagsApp;
typedef enum {
EventLoopEventFlagsOk = (1 << 0),
EventLoopEventFlagsUp = (1 << 1),
EventLoopEventFlagsDown = (1 << 2),
EventLoopEventFlagsLeft = (1 << 3),
EventLoopEventFlagsRight = (1 << 4),
EventLoopEventFlagsBack = (1 << 5),
EventLoopEventFlagsExit = (1 << 6),
} EventLoopEventFlags;
#define EVENT_LOOP_EVENT_FLAGS_MASK \
(EventLoopEventFlagsOk | EventLoopEventFlagsUp | EventLoopEventFlagsDown | \
EventLoopEventFlagsLeft | EventLoopEventFlagsRight | EventLoopEventFlagsBack | \
EventLoopEventFlagsExit)
// This function is executed in the GUI context each time an input event occurs (e.g. the user pressed a key)
static void event_loop_event_flags_app_input_callback(InputEvent* event, void* context) {
furi_assert(context);
EventLoopEventFlagsApp* app = context;
UNUSED(app);
if(event->type == InputTypePress) {
if(event->key == InputKeyOk) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsOk);
} else if(event->key == InputKeyUp) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsUp);
} else if(event->key == InputKeyDown) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsDown);
} else if(event->key == InputKeyLeft) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsLeft);
} else if(event->key == InputKeyRight) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsRight);
} else if(event->key == InputKeyBack) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsBack);
}
} else if(event->type == InputTypeLong) {
if(event->key == InputKeyBack) {
furi_event_flag_set(app->event_flag, EventLoopEventFlagsExit);
}
}
}
// This function is executed each time a new event flag is inserted in the input event flag.
static void
event_loop_event_flags_app_event_flags_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopEventFlagsApp* app = context;
furi_assert(object == app->event_flag);
EventLoopEventFlags events =
furi_event_flag_wait(app->event_flag, EVENT_LOOP_EVENT_FLAGS_MASK, FuriFlagWaitAny, 0);
furi_check((events) != 0);
if(events & EventLoopEventFlagsOk) {
FURI_LOG_I(TAG, "Press \"Ok\"");
}
if(events & EventLoopEventFlagsUp) {
FURI_LOG_I(TAG, "Press \"Up\"");
}
if(events & EventLoopEventFlagsDown) {
FURI_LOG_I(TAG, "Press \"Down\"");
}
if(events & EventLoopEventFlagsLeft) {
FURI_LOG_I(TAG, "Press \"Left\"");
}
if(events & EventLoopEventFlagsRight) {
FURI_LOG_I(TAG, "Press \"Right\"");
}
if(events & EventLoopEventFlagsBack) {
FURI_LOG_I(TAG, "Press \"Back\"");
}
if(events & EventLoopEventFlagsExit) {
FURI_LOG_I(TAG, "Exit App");
furi_event_loop_stop(app->event_loop);
}
}
static EventLoopEventFlagsApp* event_loop_event_flags_app_alloc(void) {
EventLoopEventFlagsApp* app = malloc(sizeof(EventLoopEventFlagsApp));
// Create event loop instances.
app->event_loop = furi_event_loop_alloc();
// Create event flag instances.
app->event_flag = furi_event_flag_alloc();
// Create GUI instance.
app->gui = furi_record_open(RECORD_GUI);
app->view_port = view_port_alloc();
// Gain exclusive access to the input events
view_port_input_callback_set(app->view_port, event_loop_event_flags_app_input_callback, app);
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
// Notify the event loop about incoming messages in the event flag
furi_event_loop_subscribe_event_flag(
app->event_loop,
app->event_flag,
FuriEventLoopEventIn | FuriEventLoopEventFlagEdge,
event_loop_event_flags_app_event_flags_callback,
app);
return app;
}
static void event_loop_event_flags_app_free(EventLoopEventFlagsApp* app) {
gui_remove_view_port(app->gui, app->view_port);
furi_record_close(RECORD_GUI);
app->gui = NULL;
// Delete all instances
view_port_free(app->view_port);
app->view_port = NULL;
// IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.
// Failure to do so will result in a crash.
furi_event_loop_unsubscribe(app->event_loop, app->event_flag);
furi_event_flag_free(app->event_flag);
app->event_flag = NULL;
furi_event_loop_free(app->event_loop);
app->event_loop = NULL;
free(app);
}
static void event_loop_event_flags_app_run(EventLoopEventFlagsApp* app) {
FURI_LOG_I(TAG, "Press keys to see them printed here.");
FURI_LOG_I(TAG, "Quickly press different keys to generate events.");
FURI_LOG_I(TAG, "Long press \"Back\" to exit app.");
// Run the application event loop. This call will block until the application is about to exit.
furi_event_loop_run(app->event_loop);
}
/*******************************************************************
* vvv START HERE vvv
*
* The application's entry point - referenced in application.fam
*******************************************************************/
int32_t example_event_loop_event_flags_app(void* arg) {
UNUSED(arg);
EventLoopEventFlagsApp* app = event_loop_event_flags_app_alloc();
event_loop_event_flags_app_run(app);
event_loop_event_flags_app_free(app);
return 0;
}

View File

@@ -52,7 +52,7 @@ typedef struct {
*/
// This function is executed each time the data is taken out of the stream buffer. It is used to restart the worker timer.
static bool
static void
event_loop_multi_app_stream_buffer_worker_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMultiAppWorker* worker = context;
@@ -62,8 +62,6 @@ static bool
FURI_LOG_I(TAG, "Data was removed from buffer");
// Restart the timer to generate another block of random data.
furi_event_loop_timer_start(worker->timer, WORKER_DATA_INTERVAL_MS);
return true;
}
// This function is executed when the worker timer expires. The timer will NOT restart automatically
@@ -152,7 +150,7 @@ static void event_loop_multi_app_input_callback(InputEvent* event, void* context
}
// This function is executed each time new data is available in the stream buffer.
static bool
static void
event_loop_multi_app_stream_buffer_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMultiApp* app = context;
@@ -172,12 +170,10 @@ static bool
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
furi_string_free(tmp_str);
return true;
}
// This function is executed each time a new message is inserted in the input queue.
static bool event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {
static void event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMultiApp* app = context;
@@ -222,8 +218,6 @@ static bool event_loop_multi_app_input_queue_callback(FuriEventLoopObject* objec
// Not a long press, just print the key's name.
FURI_LOG_I(TAG, "Short press: %s", input_get_key_name(event.key));
}
return true;
}
// This function is executed each time the countdown timer expires.

View File

@@ -59,7 +59,7 @@ static int32_t event_loop_mutex_app_worker_thread(void* context) {
}
// This function is being run each time when the mutex gets released
static bool event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
static void event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopMutexApp* app = context;
@@ -82,8 +82,6 @@ static bool event_loop_mutex_app_event_callback(FuriEventLoopObject* object, voi
MUTEX_EVENT_AND_FLAGS,
event_loop_mutex_app_event_callback,
app);
return true;
}
static EventLoopMutexApp* event_loop_mutex_app_alloc(void) {

View File

@@ -54,7 +54,7 @@ static int32_t event_loop_stream_buffer_app_worker_thread(void* context) {
}
// This function is being run each time when the number of bytes in the buffer is above its trigger level.
static bool
static void
event_loop_stream_buffer_app_event_callback(FuriEventLoopObject* object, void* context) {
furi_assert(context);
EventLoopStreamBufferApp* app = context;
@@ -76,8 +76,6 @@ static bool
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
furi_string_free(tmp_str);
return true;
}
static EventLoopStreamBufferApp* event_loop_stream_buffer_app_alloc(void) {