cpp template application library (#232)
This commit is contained in:
		
							
								
								
									
										92
									
								
								lib/app-template/app-template.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								lib/app-template/app-template.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <state, events> | ||||
| class AppExample : public AppTemplate<AppExampleState, AppExampleEvent> { | ||||
| 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(); | ||||
| } | ||||
							
								
								
									
										107
									
								
								lib/app-template/app-template.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								lib/app-template/app-template.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| #pragma once | ||||
| #include "callback-connector.h" | ||||
| #include "flipper.h" | ||||
| #include "flipper_v2.h" | ||||
|  | ||||
| // simple app class with template variables <state, events> | ||||
| template <class TState, class TEvent> 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 <class TState, class TEvent> AppTemplate<TState, TEvent>::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 <class TState, class TEvent> AppTemplate<TState, TEvent>::~AppTemplate() { | ||||
| } | ||||
|  | ||||
| // generic input callback | ||||
| template <class TState, class TEvent> | ||||
| void AppTemplate<TState, TEvent>::input_callback(InputEvent* input_event, void* ctx) { | ||||
|     AppTemplate* app = static_cast<AppTemplate*>(ctx); | ||||
|  | ||||
|     TEvent event; | ||||
|     event.type = TEvent::EventTypeKey; | ||||
|     event.value.input = *input_event; | ||||
|     osMessageQueuePut(app->event_queue, &event, 0, 0); | ||||
| } | ||||
|  | ||||
| // generic draw callback | ||||
| template <class TState, class TEvent> | ||||
| void AppTemplate<TState, TEvent>::draw_callback(CanvasApi* canvas, void* ctx) { | ||||
|     AppTemplate* app = static_cast<AppTemplate*>(ctx); | ||||
|     app->acquire_state(); | ||||
|  | ||||
|     canvas->clear(canvas); | ||||
|     app->render(canvas); | ||||
|  | ||||
|     app->release_state(); | ||||
| } | ||||
|  | ||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::acquire_state(void) { | ||||
|     acquire_mutex(&state_mutex, osWaitForever); | ||||
| } | ||||
|  | ||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::release_state(void) { | ||||
|     release_mutex(&state_mutex, &state); | ||||
| } | ||||
|  | ||||
| template <class TState, class TEvent> | ||||
| bool AppTemplate<TState, TEvent>::get_event(TEvent* event, uint32_t timeout) { | ||||
|     osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, timeout); | ||||
|  | ||||
|     return (event_status == osOK); | ||||
| } | ||||
|  | ||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::exit(void) { | ||||
|     // TODO remove all widgets create by app | ||||
|     widget_enabled_set(widget, false); | ||||
|     furiac_exit(NULL); | ||||
| } | ||||
|  | ||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::update_gui(void) { | ||||
|     widget_update(widget); | ||||
| } | ||||
							
								
								
									
										99
									
								
								lib/callback-connector/callback-connector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								lib/callback-connector/callback-connector.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| #ifndef CALLBACKCONNECTOR_H | ||||
| #define CALLBACKCONNECTOR_H | ||||
| #include <functional> | ||||
| namespace cbc { | ||||
| namespace Details { | ||||
|  | ||||
| template <std::size_t Tag, typename T, typename Ret, typename... Args> 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 <std::size_t Tag, typename T, typename Ret, typename... Args> | ||||
| T* FuncMemberWrapper<Tag, T, Ret, Args...>::obj{}; | ||||
|  | ||||
| template <std::size_t Tag, typename T, typename Ret, typename... Args> | ||||
| typename FuncMemberWrapper<Tag, T, Ret, Args...>::member_fun_t | ||||
|     FuncMemberWrapper<Tag, T, Ret, Args...>::member{}; | ||||
|  | ||||
| template <std::size_t Tag, typename T, typename Ret, typename... Args> | ||||
| typename FuncMemberWrapper<Tag, T, Ret, Args...>::const_member_fun_t | ||||
|     FuncMemberWrapper<Tag, T, Ret, Args...>::const_member{}; | ||||
|  | ||||
| template <typename Functor, typename Ret, typename... Args> struct FunctorWrapper { | ||||
| public: | ||||
|     static std::function<Ret(Args...)> functor; | ||||
|     static auto instatiate(Functor fn) { | ||||
|         functor = std::move(fn); | ||||
|         return MetaCall; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     static auto MetaCall(Args... args) { | ||||
|         return functor(args...); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename Functor, typename Ret, typename... Args> | ||||
| std::function<Ret(Args...)> FunctorWrapper<Functor, Ret, Args...>::functor; | ||||
|  | ||||
| template <typename Functor, typename Ret, typename T, typename... Args> | ||||
| auto deducer(Functor obj, Ret (T::*)(Args...) const) { | ||||
|     return FunctorWrapper<Functor, Ret, Args...>::instatiate(std::move(obj)); | ||||
| } | ||||
|  | ||||
| template <typename Functor, typename Ret, typename T, typename... Args> | ||||
| auto deducer(Functor obj, Ret (T::*)(Args...)) { | ||||
|     return FunctorWrapper<Functor, Ret, Args...>::instatiate(std::move(obj)); | ||||
| } | ||||
|  | ||||
| template <std::size_t tag, typename T, typename Ret, typename... Args> | ||||
| auto const_instantiate(T* t, Ret (T::*ptr)(Args...) const) { | ||||
|     return FuncMemberWrapper<tag, T, Ret, Args...>::instantiate(t, ptr); | ||||
| } | ||||
|  | ||||
| template <std::size_t tag, typename T, typename Func> auto const_instantiate(T* t, Func ptr) { | ||||
|     return const_instantiate(t, ptr); | ||||
| } | ||||
|  | ||||
| } //end of Details scope | ||||
|  | ||||
| template <std::size_t tag = 0, typename T, typename Ret, typename... Args> | ||||
| auto obtain_connector(T* t, Ret (T::*ptr)(Args...)) { | ||||
|     return Details::FuncMemberWrapper<tag, T, Ret, Args...>::instantiate(t, ptr); | ||||
| } | ||||
|  | ||||
| template <std::size_t tag = 0, typename T, typename Ret, typename... Args> | ||||
| auto obtain_connector(T* t, Ret (T::*ptr)(Args...) const) { | ||||
|     return Details::FuncMemberWrapper<tag, T, Ret, Args...>::instantiate(t, ptr); | ||||
| } | ||||
|  | ||||
| template <typename Functor> auto obtain_connector(Functor functor) { | ||||
|     return Details::deducer(std::move(functor), &Functor::operator()); | ||||
| } | ||||
| } //end of cbc scope | ||||
|  | ||||
| #endif // CALLBACKCONNECTOR_H | ||||
| @@ -45,3 +45,9 @@ C_SOURCES		+= $(wildcard $(ST25RFAL002_DIR)/*.c) | ||||
| C_SOURCES		+= $(wildcard $(ST25RFAL002_DIR)/source/*.c) | ||||
| C_SOURCES		+= $(wildcard $(ST25RFAL002_DIR)/source/st25r3916/*.c) | ||||
| endif | ||||
|  | ||||
| # callback connector (C to CPP) library | ||||
| CFLAGS			+= -I$(LIB_DIR)/callback-connector | ||||
|  | ||||
| # app template library | ||||
| CFLAGS			+= -I$(LIB_DIR)/app-template | ||||
		Reference in New Issue
	
	Block a user