fix multithread logic in template app, update gpio HAL (#250)
* fix multithread logic * more buffer for dallas id string * update apps to use new logic * delay_us small speedup * add consant qualifier to gpio records and some core api * fix some apps to use simpler method of getting gpio record * fix ibutton app, stupid stack problem
This commit is contained in:
parent
ccd40497eb
commit
a96f23af9b
@ -7,11 +7,8 @@ static void event_cb(const void* value, void* ctx) {
|
||||
const uint32_t BACKLIGHT_TIME = 10000;
|
||||
|
||||
void backlight_control(void* p) {
|
||||
// create pin
|
||||
GpioPin backlight = backlight_gpio;
|
||||
|
||||
// TODO open record
|
||||
GpioPin* backlight_record = &backlight;
|
||||
const GpioPin* backlight_record = &backlight_gpio;
|
||||
|
||||
// configure pin
|
||||
gpio_init(backlight_record, GpioModeOutputPushPull);
|
||||
|
@ -1,21 +1,22 @@
|
||||
#include "flipper_v2.h"
|
||||
|
||||
void rgb_set(bool r, bool g, bool b, GpioPin* led_r, GpioPin* led_g, GpioPin* led_b) {
|
||||
void rgb_set(
|
||||
bool r,
|
||||
bool g,
|
||||
bool b,
|
||||
const GpioPin* led_r,
|
||||
const GpioPin* led_g,
|
||||
const GpioPin* led_b) {
|
||||
gpio_write(led_r, !r);
|
||||
gpio_write(led_g, !g);
|
||||
gpio_write(led_b, !b);
|
||||
}
|
||||
|
||||
void application_blink(void* p) {
|
||||
// create pin
|
||||
GpioPin led_r = led_gpio[0];
|
||||
GpioPin led_g = led_gpio[1];
|
||||
GpioPin led_b = led_gpio[2];
|
||||
|
||||
// TODO open record
|
||||
GpioPin* led_r_record = &led_r;
|
||||
GpioPin* led_g_record = &led_g;
|
||||
GpioPin* led_b_record = &led_b;
|
||||
const GpioPin* led_r_record = &led_gpio[0];
|
||||
const GpioPin* led_g_record = &led_gpio[1];
|
||||
const GpioPin* led_b_record = &led_gpio[2];
|
||||
|
||||
// configure pin
|
||||
gpio_init(led_r_record, GpioModeOutputOpenDrain);
|
||||
|
@ -4,25 +4,21 @@
|
||||
|
||||
// start app
|
||||
void AppiButton::run() {
|
||||
acquire_state();
|
||||
mode[0] = new AppiButtonModeDallasRead(this);
|
||||
mode[1] = new AppiButtonModeDallasEmulate(this);
|
||||
release_state();
|
||||
|
||||
switch_to_mode(0);
|
||||
|
||||
// create pin
|
||||
GpioPin red_led = led_gpio[0];
|
||||
GpioPin green_led = led_gpio[1];
|
||||
|
||||
// TODO open record
|
||||
red_led_record = &red_led;
|
||||
green_led_record = &green_led;
|
||||
red_led_record = &led_gpio[0];
|
||||
green_led_record = &led_gpio[1];
|
||||
|
||||
// configure pin
|
||||
gpio_init(red_led_record, GpioModeOutputOpenDrain);
|
||||
gpio_init(green_led_record, GpioModeOutputOpenDrain);
|
||||
|
||||
app_ready();
|
||||
|
||||
AppiButtonEvent event;
|
||||
while(1) {
|
||||
if(get_event(&event, 100)) {
|
||||
@ -61,10 +57,8 @@ void AppiButton::render(CanvasApi* canvas) {
|
||||
canvas->set_font(canvas, FontPrimary);
|
||||
canvas->draw_str(canvas, 2, 12, "iButton");
|
||||
|
||||
if(mode[state.mode_index] != NULL) {
|
||||
mode[state.mode_index]->render(canvas, &state);
|
||||
}
|
||||
}
|
||||
|
||||
void AppiButton::blink_red() {
|
||||
gpio_write(red_led_record, 0);
|
||||
|
@ -38,11 +38,11 @@ public:
|
||||
// with template variables <state, events>
|
||||
class AppiButton : public AppTemplate<AppiButtonState, AppiButtonEvent> {
|
||||
public:
|
||||
GpioPin* red_led_record;
|
||||
GpioPin* green_led_record;
|
||||
const GpioPin* red_led_record;
|
||||
const GpioPin* green_led_record;
|
||||
|
||||
static const uint8_t modes_count = 2;
|
||||
AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count] = {NULL, NULL};
|
||||
AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count];
|
||||
|
||||
void run();
|
||||
void render(CanvasApi* canvas);
|
||||
|
@ -17,8 +17,7 @@ public:
|
||||
app = parent_app;
|
||||
|
||||
// TODO open record
|
||||
GpioPin one_wire_pin = ibutton_gpio;
|
||||
GpioPin* one_wire_pin_record = &one_wire_pin;
|
||||
const GpioPin* one_wire_pin_record = &ibutton_gpio;
|
||||
onewire_slave = new OneWireGpioSlave(one_wire_pin_record);
|
||||
};
|
||||
};
|
||||
@ -39,9 +38,11 @@ void AppiButtonModeDallasEmulate::render(CanvasApi* canvas, AppiButtonState* sta
|
||||
canvas->draw_str(canvas, 2, 25, "< dallas emulate");
|
||||
canvas->draw_str(canvas, 2, 37, "unimplemented");
|
||||
{
|
||||
char buf[24];
|
||||
sprintf(
|
||||
const uint8_t buffer_size = 32;
|
||||
char buf[buffer_size];
|
||||
snprintf(
|
||||
buf,
|
||||
buffer_size,
|
||||
"%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
state->dallas_address[0],
|
||||
state->dallas_address[1],
|
||||
|
@ -17,8 +17,7 @@ public:
|
||||
app = parent_app;
|
||||
|
||||
// TODO open record
|
||||
GpioPin one_wire_pin = ibutton_gpio;
|
||||
GpioPin* one_wire_pin_record = &one_wire_pin;
|
||||
const GpioPin* one_wire_pin_record = &ibutton_gpio;
|
||||
onewire = new OneWireGpio(one_wire_pin_record);
|
||||
};
|
||||
|
||||
@ -68,9 +67,11 @@ void AppiButtonModeDallasRead::render(CanvasApi* canvas, AppiButtonState* state)
|
||||
canvas->draw_str(canvas, 2, 25, "dallas read >");
|
||||
canvas->draw_str(canvas, 2, 37, "touch me, iButton");
|
||||
{
|
||||
char buf[24];
|
||||
sprintf(
|
||||
const uint8_t buffer_size = 32;
|
||||
char buf[buffer_size];
|
||||
snprintf(
|
||||
buf,
|
||||
buffer_size,
|
||||
"%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
state->dallas_address[0],
|
||||
state->dallas_address[1],
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
class OneWireGpio {
|
||||
private:
|
||||
GpioPin* gpio;
|
||||
const GpioPin* gpio;
|
||||
|
||||
public:
|
||||
OneWireGpio(GpioPin* one_wire_gpio);
|
||||
OneWireGpio(const GpioPin* one_wire_gpio);
|
||||
~OneWireGpio();
|
||||
bool reset(void);
|
||||
bool read_bit(void);
|
||||
@ -20,7 +20,7 @@ public:
|
||||
void stop(void);
|
||||
};
|
||||
|
||||
OneWireGpio::OneWireGpio(GpioPin* one_wire_gpio) {
|
||||
OneWireGpio::OneWireGpio(const GpioPin* one_wire_gpio) {
|
||||
gpio = one_wire_gpio;
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
class OneWireGpioSlave {
|
||||
private:
|
||||
GpioPin* gpio;
|
||||
const GpioPin* gpio;
|
||||
|
||||
public:
|
||||
OneWireGpioSlave(GpioPin* one_wire_gpio);
|
||||
OneWireGpioSlave(const GpioPin* one_wire_gpio);
|
||||
~OneWireGpioSlave();
|
||||
void start(void);
|
||||
void stop(void);
|
||||
@ -25,7 +25,7 @@ public:
|
||||
OneWiteTimeType wait_while_gpio(volatile OneWiteTimeType retries, const bool pin_value);
|
||||
};
|
||||
|
||||
OneWireGpioSlave::OneWireGpioSlave(GpioPin* one_wire_gpio) {
|
||||
OneWireGpioSlave::OneWireGpioSlave(const GpioPin* one_wire_gpio) {
|
||||
gpio = one_wire_gpio;
|
||||
}
|
||||
|
||||
|
@ -264,11 +264,8 @@ void irda(void* p) {
|
||||
gui->add_widget(gui, widget, GuiLayerFullscreen);
|
||||
|
||||
// Red LED
|
||||
// create pin
|
||||
GpioPin led = led_gpio[0];
|
||||
|
||||
// TODO open record
|
||||
GpioPin* led_record = &led;
|
||||
const GpioPin* led_record = &led_gpio[0];
|
||||
|
||||
// configure pin
|
||||
gpio_init(led_record, GpioModeOutputOpenDrain);
|
||||
|
@ -95,6 +95,8 @@ void SdTest::run() {
|
||||
gpio_init(red_led_record, GpioModeOutputOpenDrain);
|
||||
gpio_init(green_led_record, GpioModeOutputOpenDrain);
|
||||
|
||||
app_ready();
|
||||
|
||||
detect_sd_card();
|
||||
show_warning();
|
||||
init_sd_card();
|
||||
|
@ -77,6 +77,8 @@ void AppSdNFC::run() {
|
||||
gpio_init(red_led_record, GpioModeOutputOpenDrain);
|
||||
gpio_init(green_led_record, GpioModeOutputOpenDrain);
|
||||
|
||||
app_ready();
|
||||
|
||||
uint8_t rfal_result = rfalNfcInitialize();
|
||||
if(rfal_result) {
|
||||
set_text("rfal init fail");
|
||||
|
@ -33,7 +33,7 @@ void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout) {
|
||||
}
|
||||
}
|
||||
|
||||
bool release_mutex(ValueMutex* valuemutex, void* value) {
|
||||
bool release_mutex(ValueMutex* valuemutex, const void* value) {
|
||||
if(value != valuemutex->value) return false;
|
||||
|
||||
if(osMutexRelease(valuemutex->mutex) != osOK) return false;
|
||||
|
@ -58,7 +58,7 @@ static inline void* acquire_mutex_block(ValueMutex* valuemutex) {
|
||||
Release mutex after end of work with data.
|
||||
Call `release_mutex` and pass ValueData instance and pointer to data.
|
||||
*/
|
||||
bool release_mutex(ValueMutex* valuemutex, void* value);
|
||||
bool release_mutex(ValueMutex* valuemutex, const void* value);
|
||||
|
||||
/*
|
||||
Instead of take-access-give sequence you can use `read_mutex` and `write_mutex` functions.
|
||||
|
@ -9,7 +9,7 @@ bool gpio_api_init(void) {
|
||||
}
|
||||
|
||||
// init GPIO
|
||||
void gpio_init(GpioPin* gpio, GpioMode mode) {
|
||||
void gpio_init(const GpioPin* gpio, const GpioMode mode) {
|
||||
if(osMutexAcquire(gpioInitMutex, osWaitForever) == osOK) {
|
||||
hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow);
|
||||
osMutexRelease(gpioInitMutex);
|
||||
@ -17,13 +17,17 @@ void gpio_init(GpioPin* gpio, GpioMode mode) {
|
||||
}
|
||||
|
||||
// init GPIO, extended version
|
||||
void gpio_init_ex(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed) {
|
||||
void gpio_init_ex(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
hal_gpio_init(gpio, mode, pull, speed);
|
||||
}
|
||||
|
||||
// put GPIO to Z-state
|
||||
void gpio_disable(GpioDisableRecord* gpio_record) {
|
||||
GpioPin* gpio_pin = acquire_mutex(gpio_record->gpio_mutex, 0);
|
||||
const GpioPin* gpio_pin = acquire_mutex(gpio_record->gpio_mutex, 0);
|
||||
if(gpio_pin == NULL) {
|
||||
gpio_pin = gpio_record->gpio;
|
||||
}
|
||||
|
@ -12,13 +12,17 @@ typedef struct {
|
||||
bool gpio_api_init();
|
||||
|
||||
// init GPIO
|
||||
void gpio_init(GpioPin* gpio, GpioMode mode);
|
||||
void gpio_init(const GpioPin* gpio, const GpioMode mode);
|
||||
|
||||
// init GPIO, extended version
|
||||
void gpio_init_ex(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed);
|
||||
void gpio_init_ex(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed);
|
||||
|
||||
// write value to GPIO, false = LOW, true = HIGH
|
||||
static inline void gpio_write(GpioPin* gpio, bool state) {
|
||||
static inline void gpio_write(const GpioPin* gpio, const bool state) {
|
||||
hal_gpio_write(gpio, state);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
#include "api-hal-gpio.h"
|
||||
|
||||
// init GPIO
|
||||
void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed) {
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
// TODO: Alternate Functions
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
|
@ -39,10 +39,14 @@ typedef struct {
|
||||
} GpioPin;
|
||||
|
||||
// init GPIO
|
||||
void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed);
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed);
|
||||
|
||||
// write value to GPIO, false = LOW, true = HIGH
|
||||
static inline void hal_gpio_write(GpioPin* gpio, bool state) {
|
||||
static inline void hal_gpio_write(const GpioPin* gpio, const bool state) {
|
||||
// writing to BSSR is an atomic operation
|
||||
if(state == true) {
|
||||
gpio->port->BSRR = gpio->pin;
|
||||
|
@ -2,15 +2,18 @@
|
||||
#include "assert.h"
|
||||
#include "cmsis_os2.h"
|
||||
|
||||
static uint32_t clk_per_microsecond;
|
||||
|
||||
void delay_us_init_DWT(void) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
DWT->CYCCNT = 0U;
|
||||
clk_per_microsecond = SystemCoreClock / 1000000.0f;
|
||||
}
|
||||
|
||||
void delay_us(float microseconds) {
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
uint32_t time_ticks = microseconds * (SystemCoreClock / 1000000.0f);
|
||||
uint32_t time_ticks = microseconds * clk_per_microsecond;
|
||||
while((DWT->CYCCNT - start) < time_ticks) {
|
||||
};
|
||||
}
|
||||
|
@ -2,7 +2,11 @@
|
||||
#include "api-hal-resources.h"
|
||||
|
||||
// init GPIO
|
||||
void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed) {
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
// TODO: Alternate Functions
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
@ -16,10 +20,9 @@ void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed)
|
||||
|
||||
bool hal_gpio_read_sd_detect(void) {
|
||||
bool result = false;
|
||||
// create pin
|
||||
GpioPin sd_cs_pin = sd_cs_gpio;
|
||||
|
||||
// TODO open record
|
||||
GpioPin* sd_cs_record = &sd_cs_pin;
|
||||
const GpioPin* sd_cs_record = &sd_cs_gpio;
|
||||
|
||||
// configure pin as input
|
||||
gpio_init_ex(sd_cs_record, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
|
||||
|
@ -39,10 +39,14 @@ typedef struct {
|
||||
} GpioPin;
|
||||
|
||||
// init GPIO
|
||||
void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed);
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed);
|
||||
|
||||
// write value to GPIO, false = LOW, true = HIGH
|
||||
static inline void hal_gpio_write(GpioPin* gpio, bool state) {
|
||||
static inline void hal_gpio_write(const GpioPin* gpio, const bool state) {
|
||||
// writing to BSSR is an atomic operation
|
||||
if(state == true) {
|
||||
gpio->port->BSRR = gpio->pin;
|
||||
|
@ -2,7 +2,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
// init GPIO
|
||||
void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed){
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
// TODO more mode
|
||||
if(gpio->pin != 0) {
|
||||
switch(mode) {
|
||||
@ -28,7 +32,7 @@ void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed)
|
||||
}
|
||||
|
||||
// write value to GPIO, false = LOW, true = HIGH
|
||||
void hal_gpio_write(GpioPin* gpio, bool state){
|
||||
void hal_gpio_write(const GpioPin* gpio, const bool state) {
|
||||
if(gpio->pin != 0) {
|
||||
if(state) {
|
||||
printf("[GPIO] %s%d on\n", gpio->port, gpio->pin);
|
||||
|
@ -40,10 +40,14 @@ typedef struct {
|
||||
} GpioPin;
|
||||
|
||||
// init GPIO
|
||||
void hal_gpio_init(GpioPin* gpio, GpioMode mode, GpioPull pull, GpioSpeed speed);
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed);
|
||||
|
||||
// write value to GPIO, false = LOW, true = HIGH
|
||||
void hal_gpio_write(GpioPin* gpio, bool state);
|
||||
void hal_gpio_write(const GpioPin* gpio, const bool state);
|
||||
|
||||
// read value from GPIO, false = LOW, true = HIGH
|
||||
bool hal_gpio_read(const GpioPin* gpio);
|
@ -21,7 +21,7 @@ public:
|
||||
|
||||
// state initializer
|
||||
AppExampleState() {
|
||||
example_data = 12;
|
||||
example_data = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -51,6 +51,16 @@ public:
|
||||
|
||||
// start app
|
||||
void AppExample::run() {
|
||||
// here we dont need to acquire or release state
|
||||
// because before we call app_ready our application is "single threaded"
|
||||
state.example_data = 12;
|
||||
|
||||
// signal that we ready to render and ipc
|
||||
app_ready();
|
||||
|
||||
// from here, any data that pass in render function must be guarded
|
||||
// by calling acquire_state and release_state
|
||||
|
||||
AppExampleEvent event;
|
||||
while(1) {
|
||||
if(get_event(&event, 1000)) {
|
||||
|
@ -6,20 +6,21 @@
|
||||
// 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;
|
||||
|
||||
AppTemplate();
|
||||
~AppTemplate();
|
||||
void input_callback(InputEvent* input_event, void* ctx);
|
||||
void draw_callback(CanvasApi* canvas, void* ctx);
|
||||
virtual void render(CanvasApi* canvas) = 0;
|
||||
void acquire_state(void);
|
||||
void release_state(void);
|
||||
bool get_event(TEvent* event, uint32_t timeout);
|
||||
void app_ready(void);
|
||||
void exit(void);
|
||||
void update_gui(void);
|
||||
};
|
||||
@ -35,24 +36,15 @@ template <class TState, class TEvent> AppTemplate<TState, TEvent>::AppTemplate()
|
||||
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
|
||||
// open gui
|
||||
gui = (GuiApi*)furi_open("gui");
|
||||
if(gui == NULL) {
|
||||
printf("gui is not available\n");
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
gui->add_widget(gui, widget, GuiLayerFullscreen);
|
||||
|
||||
// allocate widget
|
||||
widget = widget_alloc();
|
||||
}
|
||||
|
||||
template <class TState, class TEvent> AppTemplate<TState, TEvent>::~AppTemplate() {
|
||||
@ -96,6 +88,24 @@ bool AppTemplate<TState, TEvent>::get_event(TEvent* event, uint32_t timeout) {
|
||||
return (event_status == osOK);
|
||||
}
|
||||
|
||||
// signal that app is ready, and we can render something
|
||||
// also unblock dependent tasks
|
||||
template <class TState, class TEvent> void AppTemplate<TState, TEvent>::app_ready(void) {
|
||||
// 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);
|
||||
|
||||
// add widget
|
||||
gui->add_widget(gui, widget, GuiLayerFullscreen);
|
||||
|
||||
// signal that our app ready to work
|
||||
furiac_ready();
|
||||
}
|
||||
|
||||
template <class TState, class TEvent> void AppTemplate<TState, TEvent>::exit(void) {
|
||||
// TODO remove all widgets create by app
|
||||
widget_enabled_set(widget, false);
|
||||
|
Loading…
Reference in New Issue
Block a user