cpp template application library (#232)
This commit is contained in:
		
							parent
							
								
									608d458b9a
								
							
						
					
					
						commit
						38d895956a
					
				
							
								
								
									
										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/*.c) | ||||||
| C_SOURCES		+= $(wildcard $(ST25RFAL002_DIR)/source/st25r3916/*.c) | C_SOURCES		+= $(wildcard $(ST25RFAL002_DIR)/source/st25r3916/*.c) | ||||||
| endif | endif | ||||||
|  | 
 | ||||||
|  | # callback connector (C to CPP) library
 | ||||||
|  | CFLAGS			+= -I$(LIB_DIR)/callback-connector | ||||||
|  | 
 | ||||||
|  | # app template library
 | ||||||
|  | CFLAGS			+= -I$(LIB_DIR)/app-template | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13