Compare commits
2 Commits
0e1e40254b
...
7299521ab1
Author | SHA1 | Date |
---|---|---|
Ondřej Hruška | 7299521ab1 | 5 years ago |
Ondřej Hruška | 8df7e693bf | 5 years ago |
@ -1,4 +1,13 @@ |
|||||||
set(COMPONENT_SRCS "app_main.c" "nokia.c" "knob.c" "gui.c" "analog.c") |
set(COMPONENT_SRCS |
||||||
set(COMPONENT_ADD_INCLUDEDIRS "") |
"app_main.c" |
||||||
|
"nokia.c" |
||||||
|
"knob.c" |
||||||
|
"gui.c" |
||||||
|
"analog.c" |
||||||
|
"liquid/liquid.c" |
||||||
|
"liquid/scene_root.c" |
||||||
|
"liquid/scene_car.c" |
||||||
|
) |
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS "liquid") |
||||||
|
|
||||||
register_component() |
register_component() |
||||||
|
@ -0,0 +1,115 @@ |
|||||||
|
#include "liquid.h" |
||||||
|
#include "rom/queue.h" |
||||||
|
#include "scenes.h" |
||||||
|
#include <malloc.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <esp_log.h> |
||||||
|
|
||||||
|
static const char *TAG = "Liquid"; |
||||||
|
|
||||||
|
struct RunningScene { |
||||||
|
struct Scene *scene; |
||||||
|
SLIST_ENTRY(RunningScene) next; |
||||||
|
} cmd_item_t; |
||||||
|
|
||||||
|
struct Liquid { |
||||||
|
SLIST_HEAD(, RunningScene) stack; // stack with the topmost scene as the first element / head
|
||||||
|
}; |
||||||
|
|
||||||
|
static struct SceneEvent Default_onChildReturn(struct Scene *scene, uint32_t tag, uint32_t status, void *data) { |
||||||
|
if (data) free(data); |
||||||
|
return SceneEvent_Repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
static void addChild(struct Liquid *container, struct Scene *child) { |
||||||
|
struct RunningScene *elm = calloc(1, sizeof(struct RunningScene)); |
||||||
|
if (!child->onChildReturn) child->onChildReturn = Default_onChildReturn; |
||||||
|
assert(child->paint != NULL); |
||||||
|
elm->scene = child; |
||||||
|
SLIST_INSERT_HEAD(&container->stack, elm, next); |
||||||
|
} |
||||||
|
|
||||||
|
struct Liquid *Liquid_start() { |
||||||
|
struct Liquid *container = calloc(1, sizeof(struct Liquid)); |
||||||
|
|
||||||
|
addChild(container, NewScene_Root()); |
||||||
|
Liquid_paint(container); |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
static struct SceneEvent handleSceneEvent(struct Liquid *container, struct SceneEvent event) { |
||||||
|
struct RunningScene *topmost = SLIST_FIRST(&container->stack); |
||||||
|
switch (event.kind) { |
||||||
|
case SceneEventKind_Close: |
||||||
|
assert(SLIST_NEXT(topmost, next) != NULL); |
||||||
|
SLIST_REMOVE_HEAD(&container->stack, next); |
||||||
|
uint32_t tag = topmost->scene->tag; |
||||||
|
|
||||||
|
if (topmost->scene->options) { |
||||||
|
free(topmost->scene->options); |
||||||
|
} |
||||||
|
free(topmost->scene); |
||||||
|
free(topmost); |
||||||
|
|
||||||
|
topmost = SLIST_FIRST(&container->stack); |
||||||
|
|
||||||
|
// this is always set.
|
||||||
|
return topmost->scene->onChildReturn(topmost->scene, tag, event.close.status, event.close.data); |
||||||
|
|
||||||
|
case SceneEventKind_OpenChild:; |
||||||
|
if (!event.open.scene) { |
||||||
|
ESP_LOGE(TAG, "Attempt to open NULL scene!"); |
||||||
|
return SceneEvent_None(); |
||||||
|
} |
||||||
|
struct Scene *newScene = event.open.scene; |
||||||
|
newScene->tag = event.open.tag; |
||||||
|
addChild(container, newScene); |
||||||
|
return SceneEvent_Repaint(); |
||||||
|
|
||||||
|
case SceneEventKind_RequestRepaint: |
||||||
|
case SceneEventKind_None: |
||||||
|
default: |
||||||
|
// this shouldn't get here
|
||||||
|
return event; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool processReturnValue(struct Liquid *container, struct SceneEvent result) { |
||||||
|
while (result.kind != SceneEventKind_None) { |
||||||
|
if (result.kind == SceneEventKind_RequestRepaint) { |
||||||
|
return 1; // repaint
|
||||||
|
} |
||||||
|
result = handleSceneEvent(container, result); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool Liquid_handleInput(struct Liquid *container, struct InputEvent event) { |
||||||
|
struct RunningScene *topmost = SLIST_FIRST(&container->stack); |
||||||
|
assert(topmost != NULL); |
||||||
|
assert(topmost->scene != NULL); |
||||||
|
if (topmost->scene->onInput) { |
||||||
|
struct SceneEvent result = topmost->scene->onInput(topmost->scene, event); |
||||||
|
return processReturnValue(container, result); |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Liquid_handleTick(struct Liquid *container) { |
||||||
|
struct RunningScene *topmost = SLIST_FIRST(&container->stack); |
||||||
|
assert(topmost != NULL); |
||||||
|
assert(topmost->scene != NULL); |
||||||
|
if (topmost->scene->onTick) { |
||||||
|
struct SceneEvent result = topmost->scene->onTick(topmost->scene); |
||||||
|
return processReturnValue(container, result); |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Liquid_paint(struct Liquid *container) { |
||||||
|
struct RunningScene *topmost = SLIST_FIRST(&container->stack); |
||||||
|
assert(topmost != NULL); |
||||||
|
topmost->scene->paint(topmost->scene); |
||||||
|
} |
@ -0,0 +1,137 @@ |
|||||||
|
/**
|
||||||
|
* GUI framework |
||||||
|
*
|
||||||
|
* Created on 2020/01/03. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef REFLOWER_LIQUID_H |
||||||
|
#define REFLOWER_LIQUID_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
struct Liquid; |
||||||
|
|
||||||
|
enum InputEvent_Kind { |
||||||
|
InputEventKind_Wheel, |
||||||
|
InputEventKind_Button, |
||||||
|
}; |
||||||
|
|
||||||
|
struct InputEvent { |
||||||
|
enum InputEvent_Kind kind; |
||||||
|
union { |
||||||
|
struct { |
||||||
|
// Wheel delta
|
||||||
|
int8_t delta; |
||||||
|
} wheel; |
||||||
|
struct { |
||||||
|
// Button state
|
||||||
|
bool state; |
||||||
|
} button; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
#define InputEvent_Wheel(delta_) ((struct InputEvent) { \ |
||||||
|
.kind = InputEventKind_Wheel, \
|
||||||
|
.wheel = { .delta = delta_ } \
|
||||||
|
}) |
||||||
|
|
||||||
|
#define InputEvent_Button(state_) ((struct InputEvent) { \ |
||||||
|
.kind = InputEventKind_Button, \
|
||||||
|
.button = { .state = state_ }, \
|
||||||
|
}) |
||||||
|
|
||||||
|
enum SceneEvent_Kind { |
||||||
|
// Close this scene.
|
||||||
|
// The scene is responsible for cleaning up 'private'.
|
||||||
|
// 'options' and the scene itself will be freed by the GUI engine.
|
||||||
|
SceneEventKind_Close, |
||||||
|
SceneEventKind_OpenChild, |
||||||
|
SceneEventKind_RequestRepaint, |
||||||
|
SceneEventKind_None, |
||||||
|
}; |
||||||
|
|
||||||
|
struct SceneEvent { |
||||||
|
enum SceneEvent_Kind kind; |
||||||
|
union { |
||||||
|
struct { |
||||||
|
// Status code
|
||||||
|
uint32_t status; |
||||||
|
// Return data on heap
|
||||||
|
void *data; |
||||||
|
} close; |
||||||
|
struct { |
||||||
|
// Scene (initialized, with options loaded in through the constructor)
|
||||||
|
void *scene; |
||||||
|
// Tag used by parent to identify the open child
|
||||||
|
uint32_t tag; |
||||||
|
} open; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Scene; |
||||||
|
|
||||||
|
#define SceneEvent_None() ((struct SceneEvent) { \ |
||||||
|
.kind = SceneEventKind_None\
|
||||||
|
}) |
||||||
|
|
||||||
|
#define SceneEvent_Repaint() ((struct SceneEvent) { \ |
||||||
|
.kind = SceneEventKind_RequestRepaint\
|
||||||
|
}) |
||||||
|
|
||||||
|
#define SceneEvent_OpenChild(child_, tag_) ((struct SceneEvent) { \ |
||||||
|
.kind = SceneEventKind_OpenChild,\
|
||||||
|
.open = { .scene = (child_), .tag=tag_ }, \
|
||||||
|
}) |
||||||
|
|
||||||
|
#define SceneEvent_Close(status_, data_) ((struct SceneEvent) { \ |
||||||
|
.kind = SceneEventKind_Close,\
|
||||||
|
.close = { .status = (status_), .data=(data_) }, \
|
||||||
|
}) |
||||||
|
|
||||||
|
typedef struct SceneEvent (*Scene_onInput_t)(struct Scene *scene, struct InputEvent event); |
||||||
|
typedef struct SceneEvent (*Scene_onChildReturn_t)(struct Scene *scene, uint32_t tag, uint32_t status, void *data); |
||||||
|
typedef struct SceneEvent (*Scene_onTick_t)(struct Scene *scene); |
||||||
|
typedef void (*Scene_repaint_t)(struct Scene *scene); |
||||||
|
|
||||||
|
struct Scene { |
||||||
|
uint32_t tag; |
||||||
|
void *options; |
||||||
|
void *private; |
||||||
|
/** Handle input */ |
||||||
|
Scene_onInput_t onInput; |
||||||
|
/** Handle child scene return */ |
||||||
|
Scene_onChildReturn_t onChildReturn; |
||||||
|
/** Periodic tick */ |
||||||
|
Scene_onTick_t onTick; |
||||||
|
/** Periodic tick */ |
||||||
|
Scene_repaint_t paint; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate scene and the inner private type. |
||||||
|
* |
||||||
|
* Requires <malloc.h> |
||||||
|
* |
||||||
|
* Must be used like: |
||||||
|
* |
||||||
|
* struct Scene *scene = SCENE_SAFE_ALLOC(struct private); |
||||||
|
* scene->onInput = ... |
||||||
|
* return scene; |
||||||
|
*/ |
||||||
|
#define SCENE_SAFE_ALLOC(private_type) \ |
||||||
|
calloc(1, sizeof(struct Scene)); \
|
||||||
|
if (!scene) return NULL; \
|
||||||
|
scene->private = calloc(1, sizeof(private_type)); \
|
||||||
|
if (!scene->private) return NULL; |
||||||
|
|
||||||
|
/** return 1 if repaint requested */ |
||||||
|
bool Liquid_handleInput(struct Liquid *container, struct InputEvent event); |
||||||
|
|
||||||
|
/** return 1 if repaint requested */ |
||||||
|
bool Liquid_handleTick(struct Liquid *container); |
||||||
|
|
||||||
|
void Liquid_paint(struct Liquid *container); |
||||||
|
struct Liquid *Liquid_start(); |
||||||
|
|
||||||
|
#endif //REFLOWER_LIQUID_H
|
@ -0,0 +1,44 @@ |
|||||||
|
#include "scenes.h" |
||||||
|
#include "liquid.h" |
||||||
|
#include "../nokia.h" |
||||||
|
#include <malloc.h> |
||||||
|
|
||||||
|
struct private { |
||||||
|
int32_t pos; |
||||||
|
}; |
||||||
|
|
||||||
|
static struct SceneEvent Car_onInput(struct Scene *scene, struct InputEvent event) { |
||||||
|
struct private *priv = scene->private; |
||||||
|
switch (event.kind) { |
||||||
|
case InputEventKind_Wheel: |
||||||
|
priv->pos += event.wheel.delta; |
||||||
|
if (priv->pos < 0) priv->pos = 0; |
||||||
|
if (priv->pos > LCD_WIDTH-21) priv->pos = LCD_WIDTH-21; |
||||||
|
return SceneEvent_Repaint(); |
||||||
|
|
||||||
|
case InputEventKind_Button: |
||||||
|
if (event.button.state) { |
||||||
|
return SceneEvent_Close(0, NULL); |
||||||
|
} |
||||||
|
// fall through
|
||||||
|
default: |
||||||
|
return SceneEvent_None(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void Car_paint(struct Scene *scene) |
||||||
|
{ |
||||||
|
struct private *priv = scene->private; |
||||||
|
|
||||||
|
LCD_clearDisplay(0); |
||||||
|
LCD_setRect(priv->pos, LCD_HEIGHT/2-10, priv->pos+20,LCD_HEIGHT/2+10,0,1); |
||||||
|
LCD_updateDisplay(); |
||||||
|
} |
||||||
|
|
||||||
|
struct Scene *NewScene_Car(void) { |
||||||
|
struct Scene *scene = SCENE_SAFE_ALLOC(struct private); |
||||||
|
|
||||||
|
scene->onInput = Car_onInput; |
||||||
|
scene->paint = Car_paint; |
||||||
|
return scene; |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
#include "scenes.h" |
||||||
|
#include "liquid.h" |
||||||
|
#include "../nokia.h" |
||||||
|
#include "../analog.h" |
||||||
|
#include <malloc.h> |
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
|
struct private { |
||||||
|
int32_t pos; |
||||||
|
uint32_t timer_phase; |
||||||
|
uint32_t timer_prescale; |
||||||
|
}; |
||||||
|
|
||||||
|
static struct SceneEvent Root_onInput(struct Scene *scene, struct InputEvent event) { |
||||||
|
struct private *priv = scene->private; |
||||||
|
switch (event.kind) { |
||||||
|
case InputEventKind_Wheel: |
||||||
|
priv->pos += event.wheel.delta; |
||||||
|
break; |
||||||
|
|
||||||
|
case InputEventKind_Button: |
||||||
|
if (event.button.state) { |
||||||
|
return SceneEvent_OpenChild(NewScene_Car(), 0); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return SceneEvent_Repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
static struct SceneEvent Root_onTick(struct Scene *scene) { |
||||||
|
struct private *priv = scene->private; |
||||||
|
priv->timer_prescale += 1; |
||||||
|
|
||||||
|
if (priv->timer_prescale == 100) { |
||||||
|
priv->timer_prescale = 0; |
||||||
|
priv->timer_phase += 1; |
||||||
|
priv->timer_phase = priv->timer_phase & 0b11; // 0..3
|
||||||
|
|
||||||
|
return SceneEvent_Repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
return SceneEvent_None(); |
||||||
|
} |
||||||
|
|
||||||
|
static void Root_paint(struct Scene *scene) |
||||||
|
{ |
||||||
|
struct private *priv = scene->private; |
||||||
|
|
||||||
|
LCD_clearDisplay(0); |
||||||
|
const char *header = "???"; |
||||||
|
switch (priv->timer_phase) { |
||||||
|
case 0: |
||||||
|
header = "ICE"; |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
header = " COLD"; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
header = "COCA"; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
header = " COLA"; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
LCD_setStr(header, 20, 3, 1); |
||||||
|
|
||||||
|
LCD_setRect(0, 15, 83, 35, 1, 1); |
||||||
|
char buf[10]; |
||||||
|
sprintf(buf, "%3d", priv->pos); |
||||||
|
LCD_setStr(buf, 2, 17, 0); |
||||||
|
sprintf(buf, "%.0f C", analog_read()); |
||||||
|
LCD_setStr(buf, 2, 26, 0); |
||||||
|
LCD_updateDisplay(); |
||||||
|
} |
||||||
|
|
||||||
|
struct Scene *NewScene_Root(void) { |
||||||
|
struct Scene *scene = SCENE_SAFE_ALLOC(struct private); |
||||||
|
|
||||||
|
scene->onInput = Root_onInput; |
||||||
|
scene->paint = Root_paint; |
||||||
|
scene->onTick = Root_onTick; |
||||||
|
return scene; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
/**
|
||||||
|
* TODO file description |
||||||
|
*
|
||||||
|
* Created on 2020/01/03. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef REFLOWER_SCENES_H |
||||||
|
#define REFLOWER_SCENES_H |
||||||
|
|
||||||
|
struct Scene *NewScene_Root(void); |
||||||
|
struct Scene *NewScene_Car(void); |
||||||
|
|
||||||
|
#endif //REFLOWER_SCENES_H
|
Loading…
Reference in new issue