From 38d895956ad06e91c7b0c5c88f5008aabc1a882f Mon Sep 17 00:00:00 2001 From: DrZlo13 Date: Thu, 12 Nov 2020 14:13:29 +0300 Subject: [PATCH] cpp template application library (#232) --- lib/app-template/app-template.cpp | 92 +++++++++++++++++ lib/app-template/app-template.h | 107 ++++++++++++++++++++ lib/callback-connector/callback-connector.h | 99 ++++++++++++++++++ lib/lib.mk | 8 +- 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 lib/app-template/app-template.cpp create mode 100644 lib/app-template/app-template.h create mode 100644 lib/callback-connector/callback-connector.h diff --git a/lib/app-template/app-template.cpp b/lib/app-template/app-template.cpp new file mode 100644 index 00000000..39fe1a9b --- /dev/null +++ b/lib/app-template/app-template.cpp @@ -0,0 +1,92 @@ +#include "flipper.h" +#include "flipper_v2.h" +#include "app-template.h" + +/* +To use this example you need to rename +AppExampleState - class to hold app state +AppExampleEvent - class to hold app event +AppExample - app class +app_cpp_example - function that launch app +*/ + +// event enumeration type +typedef uint8_t event_t; + +// app state class +class AppExampleState { +public: + // state data + uint8_t example_data; + + // state initializer + AppExampleState() { + example_data = 12; + } +}; + +// app events class +class AppExampleEvent { +public: + // events enum + static const event_t EventTypeTick = 0; + static const event_t EventTypeKey = 1; + + // payload + union { + InputEvent input; + } value; + + // event type + event_t type; +}; + +// our app derived from base AppTemplate class +// with template variables +class AppExample : public AppTemplate { +public: + void run(); + void render(CanvasApi* canvas); +}; + +// start app +void AppExample::run() { + AppExampleEvent event; + while(1) { + if(get_event(&event, 1000)) { + if(event.type == AppExampleEvent::EventTypeKey) { + // press events + if(event.value.input.state && event.value.input.input == InputBack) { + printf("bye!\n"); + exit(); + } + + if(event.value.input.state && event.value.input.input == InputUp) { + // to read or write state you need to execute + // acquire modify release state + acquire_state(); + state.example_data = 24; + release_state(); + } + } + } + + // signal to force gui update + update_gui(); + }; +} + +// render app +void AppExample::render(CanvasApi* canvas) { + // here you dont need to call acquire_state or release_state + // to read or write app state, that already handled by caller + canvas->set_color(canvas, ColorBlack); + canvas->set_font(canvas, FontPrimary); + canvas->draw_str(canvas, 2, state.example_data, "Example app"); +} + +// app enter function +extern "C" void app_cpp_example(void* p) { + AppExample* app = new AppExample(); + app->run(); +} diff --git a/lib/app-template/app-template.h b/lib/app-template/app-template.h new file mode 100644 index 00000000..cca2f62f --- /dev/null +++ b/lib/app-template/app-template.h @@ -0,0 +1,107 @@ +#pragma once +#include "callback-connector.h" +#include "flipper.h" +#include "flipper_v2.h" + +// simple app class with template variables +template class AppTemplate { +public: + AppTemplate(); + ~AppTemplate(); + void input_callback(InputEvent* input_event, void* ctx); + void draw_callback(CanvasApi* canvas, void* ctx); + virtual void render(CanvasApi* canvas) = 0; + Widget* widget; + osMessageQueueId_t event_queue; + TState state; + ValueMutex state_mutex; + GuiApi* gui; + + void acquire_state(void); + void release_state(void); + bool get_event(TEvent* event, uint32_t timeout); + void exit(void); + void update_gui(void); +}; + +template AppTemplate::AppTemplate() { + // allocate events queue + event_queue = osMessageQueueNew(10, sizeof(TEvent), NULL); + + // allocate valuemutex + // TODO: use plain os mutex? + if(!init_mutex(&state_mutex, &state, sizeof(TState))) { + printf("cannot create mutex\n"); + furiac_exit(NULL); + } + + // allocate widget + widget = widget_alloc(); + + // connect widget with input callback + auto input_cb_ref = cbc::obtain_connector(this, &AppTemplate::input_callback); + widget_input_callback_set(widget, input_cb_ref, this); + + // connect widget with draw callback + auto draw_cb_ref = cbc::obtain_connector(this, &AppTemplate::draw_callback); + widget_draw_callback_set(widget, draw_cb_ref, this); + + // open gui and add widget + gui = (GuiApi*)furi_open("gui"); + if(gui == NULL) { + printf("gui is not available\n"); + furiac_exit(NULL); + } + gui->add_widget(gui, widget, GuiLayerFullscreen); +} + +template AppTemplate::~AppTemplate() { +} + +// generic input callback +template +void AppTemplate::input_callback(InputEvent* input_event, void* ctx) { + AppTemplate* app = static_cast(ctx); + + TEvent event; + event.type = TEvent::EventTypeKey; + event.value.input = *input_event; + osMessageQueuePut(app->event_queue, &event, 0, 0); +} + +// generic draw callback +template +void AppTemplate::draw_callback(CanvasApi* canvas, void* ctx) { + AppTemplate* app = static_cast(ctx); + app->acquire_state(); + + canvas->clear(canvas); + app->render(canvas); + + app->release_state(); +} + +template void AppTemplate::acquire_state(void) { + acquire_mutex(&state_mutex, osWaitForever); +} + +template void AppTemplate::release_state(void) { + release_mutex(&state_mutex, &state); +} + +template +bool AppTemplate::get_event(TEvent* event, uint32_t timeout) { + osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, timeout); + + return (event_status == osOK); +} + +template void AppTemplate::exit(void) { + // TODO remove all widgets create by app + widget_enabled_set(widget, false); + furiac_exit(NULL); +} + +template void AppTemplate::update_gui(void) { + widget_update(widget); +} \ No newline at end of file diff --git a/lib/callback-connector/callback-connector.h b/lib/callback-connector/callback-connector.h new file mode 100644 index 00000000..e98ee914 --- /dev/null +++ b/lib/callback-connector/callback-connector.h @@ -0,0 +1,99 @@ +#ifndef CALLBACKCONNECTOR_H +#define CALLBACKCONNECTOR_H +#include +namespace cbc { +namespace Details { + +template class FuncMemberWrapper { +public: + FuncMemberWrapper() = delete; + using member_fun_t = Ret (T::*)(Args...); + using const_member_fun_t = Ret (T::*)(Args...) const; + static auto instantiate(T* t, member_fun_t ptr) { + obj = t; + member = ptr; + return MetaCall; + } + + static auto instantiate(T* t, const_member_fun_t ptr) { + obj = t; + const_member = ptr; + return ConstMetaCall; + } + +private: + static auto MetaCall(Args... args) { + return (*obj.*member)(args...); + } + static auto ConstMetaCall(Args... args) { + return (*obj.*const_member)(args...); + } + static T* obj; + static member_fun_t member; + static const_member_fun_t const_member; +}; +template +T* FuncMemberWrapper::obj{}; + +template +typename FuncMemberWrapper::member_fun_t + FuncMemberWrapper::member{}; + +template +typename FuncMemberWrapper::const_member_fun_t + FuncMemberWrapper::const_member{}; + +template struct FunctorWrapper { +public: + static std::function functor; + static auto instatiate(Functor fn) { + functor = std::move(fn); + return MetaCall; + } + +private: + static auto MetaCall(Args... args) { + return functor(args...); + } +}; + +template +std::function FunctorWrapper::functor; + +template +auto deducer(Functor obj, Ret (T::*)(Args...) const) { + return FunctorWrapper::instatiate(std::move(obj)); +} + +template +auto deducer(Functor obj, Ret (T::*)(Args...)) { + return FunctorWrapper::instatiate(std::move(obj)); +} + +template +auto const_instantiate(T* t, Ret (T::*ptr)(Args...) const) { + return FuncMemberWrapper::instantiate(t, ptr); +} + +template auto const_instantiate(T* t, Func ptr) { + return const_instantiate(t, ptr); +} + +} //end of Details scope + +template +auto obtain_connector(T* t, Ret (T::*ptr)(Args...)) { + return Details::FuncMemberWrapper::instantiate(t, ptr); +} + +template +auto obtain_connector(T* t, Ret (T::*ptr)(Args...) const) { + return Details::FuncMemberWrapper::instantiate(t, ptr); +} + +template auto obtain_connector(Functor functor) { + return Details::deducer(std::move(functor), &Functor::operator()); +} +} //end of cbc scope + +#endif // CALLBACKCONNECTOR_H \ No newline at end of file diff --git a/lib/lib.mk b/lib/lib.mk index e7670f0c..387b5962 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -44,4 +44,10 @@ CFLAGS += -I$(ST25RFAL002_DIR)/source/st25r3916 C_SOURCES += $(wildcard $(ST25RFAL002_DIR)/*.c) C_SOURCES += $(wildcard $(ST25RFAL002_DIR)/source/*.c) C_SOURCES += $(wildcard $(ST25RFAL002_DIR)/source/st25r3916/*.c) -endif \ No newline at end of file +endif + +# callback connector (C to CPP) library +CFLAGS += -I$(LIB_DIR)/callback-connector + +# app template library +CFLAGS += -I$(LIB_DIR)/app-template \ No newline at end of file