cpp template application library (#232)

This commit is contained in:
DrZlo13 2020-11-12 14:13:29 +03:00 committed by GitHub
parent 608d458b9a
commit 38d895956a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 305 additions and 1 deletions

View 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();
}

View 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);
}

View 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

View File

@ -44,4 +44,10 @@ CFLAGS += -I$(ST25RFAL002_DIR)/source/st25r3916
C_SOURCES += $(wildcard $(ST25RFAL002_DIR)/*.c) 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