Просмотр исходного кода

cpp template application library (#232)

DrZlo13 5 лет назад
Родитель
Сommit
38d895956a

+ 92 - 0
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 <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 - 0
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 <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 - 0
lib/callback-connector/callback-connector.h

@@ -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

+ 7 - 1
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
+endif
+
+# callback connector (C to CPP) library
+CFLAGS			+= -I$(LIB_DIR)/callback-connector
+
+# app template library
+CFLAGS			+= -I$(LIB_DIR)/app-template