mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-10 05:59:08 -07:00
[FL-3833] Furi: event loop (#3675)
* Furi: epoll prototype * Gui: simplify view_dispatcher custom event processing * Furi: add missing critical sections to epoll * Furi: add epoll unit tests, fully implement level processing for in and out events * Furi: properly trigger epoll item event on adding mq, update tests. * Unit tests: cleanup defines * Furi: protect epoll from modification in callback * Furi: rename epoll into event_loop, cleanup api naming * Sync API Symbols * Furi: add event loop contract and link api, port mq to new api, cleanup code * Format Sources * Furi: cleanup mq and event loop code * Furi: remove unused staff from message queue * ApiSymbols: remove event loop from public APIs. * Fix furi unit tests --------- 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:
164
applications/debug/unit_tests/tests/furi/furi_event_loop.c
Normal file
164
applications/debug/unit_tests/tests/furi/furi_event_loop.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "../test.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "TestFuriEventLoop"
|
||||
|
||||
#define EVENT_LOOP_EVENT_COUNT (256u)
|
||||
|
||||
typedef struct {
|
||||
FuriMessageQueue* mq;
|
||||
|
||||
FuriEventLoop* producer_event_loop;
|
||||
uint32_t producer_counter;
|
||||
|
||||
FuriEventLoop* consumer_event_loop;
|
||||
uint32_t consumer_counter;
|
||||
} TestFuriData;
|
||||
|
||||
bool test_furi_event_loop_producer_mq_callback(FuriMessageQueue* queue, void* context) {
|
||||
furi_check(context);
|
||||
|
||||
TestFuriData* data = context;
|
||||
furi_check(data->mq == queue, "Invalid queue");
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG, "producer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter);
|
||||
|
||||
// Remove and add should not cause crash
|
||||
// if(data->producer_counter == EVENT_LOOP_EVENT_COUNT/2) {
|
||||
// furi_event_loop_message_queue_remove(data->producer_event_loop, data->mq);
|
||||
// furi_event_loop_message_queue_add(
|
||||
// data->producer_event_loop,
|
||||
// data->mq,
|
||||
// FuriEventLoopEventOut,
|
||||
// test_furi_event_loop_producer_mq_callback,
|
||||
// data);
|
||||
// }
|
||||
|
||||
if(data->producer_counter == EVENT_LOOP_EVENT_COUNT) {
|
||||
furi_event_loop_stop(data->producer_event_loop);
|
||||
return false;
|
||||
}
|
||||
|
||||
data->producer_counter++;
|
||||
furi_check(
|
||||
furi_message_queue_put(data->mq, &data->producer_counter, 0) == FuriStatusOk,
|
||||
"furi_message_queue_put failed");
|
||||
furi_delay_us(furi_hal_random_get() % 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t test_furi_event_loop_producer(void* p) {
|
||||
furi_check(p);
|
||||
|
||||
FURI_LOG_I(TAG, "producer start");
|
||||
|
||||
TestFuriData* data = p;
|
||||
|
||||
data->producer_event_loop = furi_event_loop_alloc();
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
data->producer_event_loop,
|
||||
data->mq,
|
||||
FuriEventLoopEventOut,
|
||||
test_furi_event_loop_producer_mq_callback,
|
||||
data);
|
||||
|
||||
furi_event_loop_run(data->producer_event_loop);
|
||||
|
||||
furi_event_loop_message_queue_unsubscribe(data->producer_event_loop, data->mq);
|
||||
furi_event_loop_free(data->producer_event_loop);
|
||||
|
||||
FURI_LOG_I(TAG, "producer end");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool test_furi_event_loop_consumer_mq_callback(FuriMessageQueue* queue, void* context) {
|
||||
furi_check(context);
|
||||
|
||||
TestFuriData* data = context;
|
||||
furi_check(data->mq == queue);
|
||||
|
||||
furi_delay_us(furi_hal_random_get() % 1000);
|
||||
furi_check(furi_message_queue_get(data->mq, &data->consumer_counter, 0) == FuriStatusOk);
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG, "consumer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter);
|
||||
|
||||
// Remove and add should not cause crash
|
||||
// if(data->producer_counter == EVENT_LOOP_EVENT_COUNT/2) {
|
||||
// furi_event_loop_message_queue_remove(data->consumer_event_loop, data->mq);
|
||||
// furi_event_loop_message_queue_add(
|
||||
// data->consumer_event_loop,
|
||||
// data->mq,
|
||||
// FuriEventLoopEventIn,
|
||||
// test_furi_event_loop_producer_mq_callback,
|
||||
// data);
|
||||
// }
|
||||
|
||||
if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT) {
|
||||
furi_event_loop_stop(data->consumer_event_loop);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t test_furi_event_loop_consumer(void* p) {
|
||||
furi_check(p);
|
||||
|
||||
FURI_LOG_I(TAG, "consumer start");
|
||||
|
||||
TestFuriData* data = p;
|
||||
|
||||
data->consumer_event_loop = furi_event_loop_alloc();
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
data->consumer_event_loop,
|
||||
data->mq,
|
||||
FuriEventLoopEventIn,
|
||||
test_furi_event_loop_consumer_mq_callback,
|
||||
data);
|
||||
|
||||
furi_event_loop_run(data->consumer_event_loop);
|
||||
|
||||
furi_event_loop_message_queue_unsubscribe(data->consumer_event_loop, data->mq);
|
||||
furi_event_loop_free(data->consumer_event_loop);
|
||||
|
||||
FURI_LOG_I(TAG, "consumer end");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_furi_event_loop(void) {
|
||||
TestFuriData data = {};
|
||||
|
||||
data.mq = furi_message_queue_alloc(16, sizeof(uint32_t));
|
||||
|
||||
FuriThread* producer_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(producer_thread, "producer_thread");
|
||||
furi_thread_set_stack_size(producer_thread, 1 * 1024);
|
||||
furi_thread_set_callback(producer_thread, test_furi_event_loop_producer);
|
||||
furi_thread_set_context(producer_thread, &data);
|
||||
furi_thread_start(producer_thread);
|
||||
|
||||
FuriThread* consumer_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(consumer_thread, "consumer_thread");
|
||||
furi_thread_set_stack_size(consumer_thread, 1 * 1024);
|
||||
furi_thread_set_callback(consumer_thread, test_furi_event_loop_consumer);
|
||||
furi_thread_set_context(consumer_thread, &data);
|
||||
furi_thread_start(consumer_thread);
|
||||
|
||||
// Wait for thread to complete their tasks
|
||||
furi_thread_join(producer_thread);
|
||||
furi_thread_join(consumer_thread);
|
||||
|
||||
// The test itself
|
||||
mu_assert_int_eq(data.producer_counter, data.consumer_counter);
|
||||
|
||||
// Release memory
|
||||
furi_thread_free(consumer_thread);
|
||||
furi_thread_free(producer_thread);
|
||||
furi_message_queue_free(data.mq);
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
void test_furi_create_open(void);
|
||||
void test_furi_concurrent_access(void);
|
||||
void test_furi_pubsub(void);
|
||||
|
||||
void test_furi_memmgr(void);
|
||||
void test_furi_event_loop(void);
|
||||
|
||||
static int foo = 0;
|
||||
|
||||
@@ -38,15 +38,19 @@ MU_TEST(mu_test_furi_memmgr) {
|
||||
test_furi_memmgr();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_event_loop) {
|
||||
test_furi_event_loop();
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_suite) {
|
||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||
|
||||
MU_RUN_TEST(test_check);
|
||||
|
||||
// v2 tests
|
||||
MU_RUN_TEST(mu_test_furi_create_open);
|
||||
MU_RUN_TEST(mu_test_furi_pubsub);
|
||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||
MU_RUN_TEST(mu_test_furi_event_loop);
|
||||
}
|
||||
|
||||
int run_minunit_test_furi(void) {
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
|
||||
#include <rpc/rpc_i.h>
|
||||
#include <flipper.pb.h>
|
||||
#include <core/event_loop.h>
|
||||
|
||||
static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
|
||||
API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)),
|
||||
API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)),
|
||||
API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char* filename)),
|
||||
API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char*)),
|
||||
API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)),
|
||||
API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)),
|
||||
API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)),
|
||||
@@ -26,4 +27,17 @@ static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
|
||||
xQueueGenericSend,
|
||||
BaseType_t,
|
||||
(QueueHandle_t, const void* const, TickType_t, const BaseType_t)),
|
||||
API_METHOD(furi_event_loop_alloc, FuriEventLoop*, (void)),
|
||||
API_METHOD(furi_event_loop_free, void, (FuriEventLoop*)),
|
||||
API_METHOD(
|
||||
furi_event_loop_message_queue_subscribe,
|
||||
void,
|
||||
(FuriEventLoop*,
|
||||
FuriMessageQueue*,
|
||||
FuriEventLoopEvent,
|
||||
FuriEventLoopMessageQueueCallback,
|
||||
void*)),
|
||||
API_METHOD(furi_event_loop_message_queue_unsubscribe, void, (FuriEventLoop*, FuriMessageQueue*)),
|
||||
API_METHOD(furi_event_loop_run, void, (FuriEventLoop*)),
|
||||
API_METHOD(furi_event_loop_stop, void, (FuriEventLoop*)),
|
||||
API_VARIABLE(PB_Main_msg, PB_Main_msg_t)));
|
||||
|
||||
@@ -29,8 +29,18 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
||||
// Free ViewPort
|
||||
view_port_free(view_dispatcher->view_port);
|
||||
// Free internal queue
|
||||
if(view_dispatcher->queue) {
|
||||
furi_message_queue_free(view_dispatcher->queue);
|
||||
if(view_dispatcher->input_queue) {
|
||||
furi_event_loop_message_queue_unsubscribe(
|
||||
view_dispatcher->event_loop, view_dispatcher->input_queue);
|
||||
furi_message_queue_free(view_dispatcher->input_queue);
|
||||
}
|
||||
if(view_dispatcher->event_queue) {
|
||||
furi_event_loop_message_queue_unsubscribe(
|
||||
view_dispatcher->event_loop, view_dispatcher->event_queue);
|
||||
furi_message_queue_free(view_dispatcher->event_queue);
|
||||
}
|
||||
if(view_dispatcher->event_loop) {
|
||||
furi_event_loop_free(view_dispatcher->event_loop);
|
||||
}
|
||||
// Free dispatcher
|
||||
free(view_dispatcher);
|
||||
@@ -38,8 +48,25 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
||||
|
||||
void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(view_dispatcher->queue == NULL);
|
||||
view_dispatcher->queue = furi_message_queue_alloc(16, sizeof(ViewDispatcherMessage));
|
||||
furi_check(view_dispatcher->event_loop == NULL);
|
||||
|
||||
view_dispatcher->event_loop = furi_event_loop_alloc();
|
||||
|
||||
view_dispatcher->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
view_dispatcher->event_loop,
|
||||
view_dispatcher->input_queue,
|
||||
FuriEventLoopEventIn,
|
||||
view_dispatcher_run_input_callback,
|
||||
view_dispatcher);
|
||||
|
||||
view_dispatcher->event_queue = furi_message_queue_alloc(8, sizeof(uint32_t));
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
view_dispatcher->event_loop,
|
||||
view_dispatcher->event_queue,
|
||||
FuriEventLoopEventIn,
|
||||
view_dispatcher_run_event_callback,
|
||||
view_dispatcher);
|
||||
}
|
||||
|
||||
void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) {
|
||||
@@ -70,48 +97,45 @@ void view_dispatcher_set_tick_event_callback(
|
||||
view_dispatcher->tick_period = tick_period;
|
||||
}
|
||||
|
||||
FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(view_dispatcher->event_loop);
|
||||
|
||||
return view_dispatcher->event_loop;
|
||||
}
|
||||
|
||||
void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(view_dispatcher->queue);
|
||||
furi_check(view_dispatcher->event_loop);
|
||||
|
||||
uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever :
|
||||
view_dispatcher->tick_period;
|
||||
ViewDispatcherMessage message;
|
||||
while(1) {
|
||||
if(furi_message_queue_get(view_dispatcher->queue, &message, tick_period) != FuriStatusOk) {
|
||||
view_dispatcher_handle_tick_event(view_dispatcher);
|
||||
continue;
|
||||
}
|
||||
if(message.type == ViewDispatcherMessageTypeStop) {
|
||||
break;
|
||||
} else if(message.type == ViewDispatcherMessageTypeInput) {
|
||||
view_dispatcher_handle_input(view_dispatcher, &message.input);
|
||||
} else if(message.type == ViewDispatcherMessageTypeCustomEvent) {
|
||||
view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event);
|
||||
}
|
||||
}
|
||||
|
||||
furi_event_loop_tick_set(
|
||||
view_dispatcher->event_loop,
|
||||
tick_period,
|
||||
view_dispatcher_handle_tick_event,
|
||||
view_dispatcher);
|
||||
|
||||
furi_event_loop_run(view_dispatcher->event_loop);
|
||||
|
||||
// Wait till all input events delivered
|
||||
InputEvent input;
|
||||
while(view_dispatcher->ongoing_input) {
|
||||
furi_message_queue_get(view_dispatcher->queue, &message, FuriWaitForever);
|
||||
if(message.type == ViewDispatcherMessageTypeInput) {
|
||||
uint8_t key_bit = (1 << message.input.key);
|
||||
if(message.input.type == InputTypePress) {
|
||||
view_dispatcher->ongoing_input |= key_bit;
|
||||
} else if(message.input.type == InputTypeRelease) {
|
||||
view_dispatcher->ongoing_input &= ~key_bit;
|
||||
}
|
||||
furi_message_queue_get(view_dispatcher->input_queue, &input, FuriWaitForever);
|
||||
uint8_t key_bit = (1 << input.key);
|
||||
if(input.type == InputTypePress) {
|
||||
view_dispatcher->ongoing_input |= key_bit;
|
||||
} else if(input.type == InputTypeRelease) {
|
||||
view_dispatcher->ongoing_input &= ~key_bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void view_dispatcher_stop(ViewDispatcher* view_dispatcher) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(view_dispatcher->queue);
|
||||
ViewDispatcherMessage message;
|
||||
message.type = ViewDispatcherMessageTypeStop;
|
||||
furi_check(
|
||||
furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
furi_check(view_dispatcher->event_loop);
|
||||
furi_event_loop_stop(view_dispatcher->event_loop);
|
||||
}
|
||||
|
||||
void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) {
|
||||
@@ -218,12 +242,9 @@ void view_dispatcher_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
void view_dispatcher_input_callback(InputEvent* event, void* context) {
|
||||
ViewDispatcher* view_dispatcher = context;
|
||||
if(view_dispatcher->queue) {
|
||||
ViewDispatcherMessage message;
|
||||
message.type = ViewDispatcherMessageTypeInput;
|
||||
message.input = *event;
|
||||
if(view_dispatcher->input_queue) {
|
||||
furi_check(
|
||||
furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) ==
|
||||
furi_message_queue_put(view_dispatcher->input_queue, event, FuriWaitForever) ==
|
||||
FuriStatusOk);
|
||||
} else {
|
||||
view_dispatcher_handle_input(view_dispatcher, event);
|
||||
@@ -287,7 +308,8 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
|
||||
}
|
||||
}
|
||||
|
||||
void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher) {
|
||||
void view_dispatcher_handle_tick_event(void* context) {
|
||||
ViewDispatcher* view_dispatcher = context;
|
||||
if(view_dispatcher->tick_event_callback) {
|
||||
view_dispatcher->tick_event_callback(view_dispatcher->event_context);
|
||||
}
|
||||
@@ -306,14 +328,11 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32
|
||||
|
||||
void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(view_dispatcher->queue);
|
||||
|
||||
ViewDispatcherMessage message;
|
||||
message.type = ViewDispatcherMessageTypeCustomEvent;
|
||||
message.custom_event = event;
|
||||
furi_check(view_dispatcher->event_loop);
|
||||
|
||||
furi_check(
|
||||
furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
furi_message_queue_put(view_dispatcher->event_queue, &event, FuriWaitForever) ==
|
||||
FuriStatusOk);
|
||||
}
|
||||
|
||||
static const ViewPortOrientation view_dispatcher_view_port_orientation_table[] = {
|
||||
@@ -345,7 +364,7 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie
|
||||
view_port_update(view_dispatcher->view_port);
|
||||
} else {
|
||||
view_port_enabled_set(view_dispatcher->view_port, false);
|
||||
if(view_dispatcher->queue) {
|
||||
if(view_dispatcher->event_loop) {
|
||||
view_dispatcher_stop(view_dispatcher);
|
||||
}
|
||||
}
|
||||
@@ -361,3 +380,27 @@ void view_dispatcher_update(View* view, void* context) {
|
||||
view_port_update(view_dispatcher->view_port);
|
||||
}
|
||||
}
|
||||
|
||||
bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context) {
|
||||
furi_assert(context);
|
||||
ViewDispatcher* instance = context;
|
||||
furi_assert(instance->event_queue == queue);
|
||||
|
||||
uint32_t event;
|
||||
furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk);
|
||||
view_dispatcher_handle_custom_event(instance, event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context) {
|
||||
furi_assert(context);
|
||||
ViewDispatcher* instance = context;
|
||||
furi_assert(instance->input_queue == queue);
|
||||
|
||||
InputEvent input;
|
||||
furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk);
|
||||
view_dispatcher_handle_input(instance, &input);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher);
|
||||
|
||||
/** Enable queue support
|
||||
*
|
||||
* If queue enabled all input and custom events will be dispatched throw
|
||||
* internal queue
|
||||
* Allocates event_loop, input and event message queues. Must be used with
|
||||
* `view_dispatcher_run`
|
||||
*
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
*/
|
||||
@@ -101,6 +101,20 @@ void view_dispatcher_set_tick_event_callback(
|
||||
*/
|
||||
void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context);
|
||||
|
||||
/** Get event_loop instance
|
||||
*
|
||||
* event_loop instance is allocated on `view_dispatcher_enable_queue` and used
|
||||
* in view_dispatcher_run.
|
||||
*
|
||||
* You can add your objects into event_loop instance, but don't run the loop on
|
||||
* your side it will cause issues with input processing on dispatcher stop.
|
||||
*
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
*
|
||||
* @return The event_loop instance.
|
||||
*/
|
||||
FuriEventLoop* view_dispatcher_get_event_loop(ViewDispatcher* view_dispatcher);
|
||||
|
||||
/** Run ViewDispatcher
|
||||
*
|
||||
* Use only after queue enabled
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
#include "view_dispatcher.h"
|
||||
@@ -15,7 +14,10 @@
|
||||
DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST)
|
||||
|
||||
struct ViewDispatcher {
|
||||
FuriMessageQueue* queue;
|
||||
FuriEventLoop* event_loop;
|
||||
FuriMessageQueue* input_queue;
|
||||
FuriMessageQueue* event_queue;
|
||||
|
||||
Gui* gui;
|
||||
ViewPort* view_port;
|
||||
ViewDict_t views;
|
||||
@@ -32,20 +34,6 @@ struct ViewDispatcher {
|
||||
void* event_context;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ViewDispatcherMessageTypeInput,
|
||||
ViewDispatcherMessageTypeCustomEvent,
|
||||
ViewDispatcherMessageTypeStop,
|
||||
} ViewDispatcherMessageType;
|
||||
|
||||
typedef struct {
|
||||
ViewDispatcherMessageType type;
|
||||
union {
|
||||
InputEvent input;
|
||||
uint32_t custom_event;
|
||||
};
|
||||
} ViewDispatcherMessage;
|
||||
|
||||
/** ViewPort Draw Callback */
|
||||
void view_dispatcher_draw_callback(Canvas* canvas, void* context);
|
||||
|
||||
@@ -56,7 +44,7 @@ void view_dispatcher_input_callback(InputEvent* event, void* context);
|
||||
void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event);
|
||||
|
||||
/** Tick handler */
|
||||
void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher);
|
||||
void view_dispatcher_handle_tick_event(void* context);
|
||||
|
||||
/** Custom event handler */
|
||||
void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event);
|
||||
@@ -66,3 +54,9 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie
|
||||
|
||||
/** ViewDispatcher update event */
|
||||
void view_dispatcher_update(View* view, void* context);
|
||||
|
||||
/** ViewDispatcher run event loop event callback */
|
||||
bool view_dispatcher_run_event_callback(FuriMessageQueue* queue, void* context);
|
||||
|
||||
/** ViewDispatcher run event loop input callback */
|
||||
bool view_dispatcher_run_input_callback(FuriMessageQueue* queue, void* context);
|
||||
|
||||
Reference in New Issue
Block a user