add manual controls menu, remove demu screens

master
Ondřej Hruška 4 years ago
parent 17a228faa7
commit 202b9e49f7
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 4
      main/CMakeLists.txt
  2. 2
      main/analog.h
  3. 28
      main/graphics/drawing.c
  4. 5
      main/graphics/drawing.h
  5. 11
      main/graphics/utf8.c
  6. 2
      main/graphics/utf8.h
  7. 19
      main/liquid/gui.c
  8. 45
      main/scenes/scene_car.c
  9. 93
      main/scenes/scene_demo.c
  10. 112
      main/scenes/scene_manual_menu.c
  11. 67
      main/scenes/scene_menu.c
  12. 12
      main/scenes/scene_menu.h
  13. 93
      main/scenes/scene_test_menu.c
  14. 2
      main/scenes/scenes.h

@ -5,12 +5,10 @@ set(COMPONENT_SRCS
"analog.c"
"liquid/liquid.c"
"scenes/scene_root.c"
"scenes/scene_car.c"
"scenes/scene_demo.c"
"scenes/scene_bootanim.c"
"scenes/scene_menu.c"
"scenes/scene_number.c"
"scenes/scene_test_menu.c"
"scenes/scene_manual_menu.c"
"graphics/nokia.c"
"graphics/utf8.c"
"graphics/font.c"

@ -7,6 +7,8 @@
#ifndef REFLOWER_ANALOG_H
#define REFLOWER_ANALOG_H
#include <stdint.h>
#define ANALOG_SAMPLE_TIME_MS 1000
#define REG_HISTORY_LEN 241

@ -242,27 +242,37 @@ void LCD_setStrEx(const char *dString, int x, int y, enum Color color, struct Te
int spacingy = style.spacing_y;
struct Utf8Char uchar;
int limit = style.max_chars;
int limit = style.limit;
int skip = style.skip;
bool wrap;
while ((uchar = Utf8Iterator_Next(&iter)).uint) {
LCD_setCharEx(uchar, x, y, color, style);
if (skip > 0) {
skip -= 1;
continue;
}
wrap = uchar.bytes[0] == '\n';
if (!wrap) LCD_setCharEx(uchar, x, y, color, style);
if (limit > 0) {
limit -= 1;
if (limit == 0) return;
}
x += charw;
if (style.bg) {
for (int i = y; i < y + charh; i++) {
for (int j = x; j < x + spacingx; j++) {
LCD_setPixel(j, i, !color);
if (!wrap) {
x += charw;
if (style.bg) {
for (int i = y; i < y + charh; i++) {
for (int j = x; j < x + spacingx; j++) {
LCD_setPixel(j, i, !color);
}
}
}
}
x += spacingx;
if (x > (LCD_WIDTH - charw)) {
if (wrap || x > (LCD_WIDTH - charw)) {
if (style.nowrap) break;
if (style.wrap_to_0) {
x = 0;

@ -51,9 +51,10 @@ enum TextSize {
};
struct TextStyle {
bool bg; //!< Fill the characters background with the opposite color
bool bg; //!< Fill the characters background with the opposite color
enum TextSize size; //!< Character size
int max_chars; //!< Number of characters to print from the string, if > 0
int limit; //!< Number of characters to print from the string, if > 0
int skip; //!< Characters to skip before printing
int spacing_x; //!< Additional X spacing
int spacing_y; //!< Additional Y spacing
bool nowrap; //!< Stop painting when the right edge of the screen is reached

@ -21,6 +21,17 @@ const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) {.uint = 0};
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
size_t utf8_strlen(const char *text)
{
// TODO optimize
struct Utf8Iterator iter;
Utf8Iterator_Init(&iter, text);
size_t num = 0;
while ((Utf8Iterator_Next(&iter)).uint) {
num++;
}
return num;
}
/**
* Handle a received character

@ -63,6 +63,8 @@ static inline void Utf8Iterator_Init(struct Utf8Iterator *self, const char *sour
self->source = source;
}
size_t utf8_strlen(const char *text);
/**
* Get the next character from the iterator; Returns empty character if there are no more characters to parse.
*

@ -8,10 +8,8 @@
#include "drawing.h"
static void gui_thread(void *arg);
static void gui_tick(TimerHandle_t xTimer);
TaskHandle_t hGuiThread = NULL;
static TimerHandle_t hTicker = NULL;
void gui_init() {
printf("GUI init\n");
@ -20,20 +18,6 @@ void gui_init() {
int rv = xTaskCreate(gui_thread, "gui", 4096, NULL, 6, &hGuiThread);
assert (rv == pdPASS);
hTicker = xTimerCreate(
"gui_tick", /* name */
pdMS_TO_TICKS(10), /* period/time */
pdTRUE, /* auto reload */
(void*)0, /* timer ID */
gui_tick); /* callback */
assert(hTicker != NULL);
xTimerStart(hTicker, portMAX_DELAY);
}
static void gui_tick(TimerHandle_t xTimer) {
xTaskNotify(hGuiThread, 0b10000, eSetBits);
}
/**
@ -59,8 +43,7 @@ static void __attribute__((noreturn)) gui_thread(void *arg) {
xTaskNotifyWait(0, ULONG_MAX, &value, pdMS_TO_TICKS(10));
uint32_t time = xTaskGetTickCount();
if (value & 0b10000) {
// TICK
if (time - last_time >= pdMS_TO_TICKS(2)) {
want_repaint |= Liquid_handleTick(liquid, time - last_time);
last_time = time;
}

@ -1,45 +0,0 @@
#include <malloc.h>
#include <inttypes.h>
#include "graphics/nokia.h"
#include "graphics/drawing.h"
#include "scenes.h"
#include "liquid.h"
struct CarScene {
struct Scene base;
int32_t pos;
};
static struct SceneEvent onInput(struct CarScene *self, struct InputEvent event) {
switch (event.kind) {
case InputEventKind_Wheel:
self->pos += event.wheel.delta;
if (self->pos < 0) self->pos = 0;
if (self->pos > LCD_WIDTH-21) self->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 paint(struct CarScene *self)
{
LCD_clearDisplay(0);
LCD_setRect(self->pos, LCD_HEIGHT/2-10, self->pos+20,LCD_HEIGHT/2+10,0,1);
LCD_updateDisplay();
}
struct Scene *NewScene_Car(void) {
struct CarScene *scene = calloc(1, sizeof(struct CarScene));
if (!scene) return NULL;
scene->base.onInput = (Scene_onInput_t) onInput;
scene->base.paint = (Scene_paint_t) paint;
return (struct Scene *) scene;
}

@ -1,93 +0,0 @@
#include <malloc.h>
#include <stdio.h>
#include <graphics/bitmaps.h>
#include "scenes.h"
#include "liquid.h"
#include "analog.h"
#include "graphics/nokia.h"
#include "graphics/drawing.h"
/**
* The struct is allocated bigger than 'Scene' to accommodate private fields.
* Since the base struct is located at the beginning, it can be cast and passed around as Scene.
*/
struct DemoScene {
struct Scene base;
int32_t pos;
uint32_t timer_phase;
uint32_t timer_prescale;
};
static struct SceneEvent onInput(struct DemoScene *self, struct InputEvent event) {
switch (event.kind) {
case InputEventKind_Wheel:
self->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 onTick(struct DemoScene *self, uint32_t millis) {
// top text anim
self->timer_prescale += millis;
if (self->timer_prescale == 1000) {
self->timer_prescale = 0;
self->timer_phase += 1;
self->timer_phase = self->timer_phase & 0b11; // 0..3
return SceneEvent_Repaint();
}
return SceneEvent_None();
}
static void paint(struct DemoScene *self)
{
LCD_clearDisplay(0);
const char *header = "???";
switch (self->timer_phase) {
case 0:
header = "Drink water";
break;
case 1:
header = " Drink water";
break;
case 2:
header = " Drink water";
break;
case 3:
header = " Drink water";
break;
}
LCD_setStrEx(header, 3, 1, BLACK, (struct TextStyle) {.size = FONT_BOLD});
LCD_setRect(0, 12, 83, 31, true, BLACK);
char buf[10];
// sprintf(buf, "%3d", priv->pos);
// LCD_setStr(buf, 2, 15, 0);
sprintf(buf, "🌡%.0f°C", analog_read());
LCD_setStrEx(buf, 2, 14, WHITE, (struct TextStyle) {.size = FONT_DOUBLE});
LCD_setStr("×↑↓←→⏰⌛☸⏎🌡°🔙▶◀", 2, 33, BLACK);
}
struct Scene *NewScene_Demo(void) {
struct DemoScene *scene = calloc(1, sizeof(struct DemoScene));
scene->base.onInput = (Scene_onInput_t) onInput;
scene->base.paint = (Scene_paint_t) paint;
scene->base.onTick = (Scene_onTick_t) onTick;
scene->base.onInput = (Scene_onInput_t) onInput;
return (struct Scene *) scene;
}

@ -0,0 +1,112 @@
#include <malloc.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <analog.h>
#include <firehazard.h>
#include "liquid.h"
#include "graphics/drawing.h"
#include "graphics/display_spec.h"
#include "scenes.h"
#include "scene_menu.h"
struct Priv {
Scene_paint_t basePaint;
};
static struct SceneEvent onSelect(struct MenuScene *self) {
if (self->selected == 0) {
struct NumberSceneOpts *opts = calloc(1, sizeof(struct NumberSceneOpts));
strcpy(opts->unit, "°C");
strcpy(opts->label, "Setpoint");
opts->value = (int) roundf(fire_get_setpoint(false));
opts->min = 0;
opts->max = 350;
return SceneEvent_OpenChild(NewScene_Number(opts), 0);
}
if (self->selected == 1) {
fire_enable(!fire_enabled());
return SceneEvent_Repaint();
}
return SceneEvent_None();
}
static void priv_deinit(struct MenuScene *self)
{
free(self->extra);
self->extra = NULL;
}
static struct SceneEvent onChildReturn(struct MenuScene *self, uint32_t tag, int32_t status, void *data)
{
if (tag == 0) {
fire_setlevel((float) status);
}
return SceneEvent_Repaint();
}
#define XLINE 38
void print_labels(struct MenuItem *items) {
snprintf(items[0].label, MENUITEM_LABEL_LEN, "▶%.0f°C", fire_get_setpoint(false));
if (fire_enabled()) {
strncpy(items[1].label, "▶STOP", MENUITEM_LABEL_LEN);
} else {
strncpy(items[1].label, "▶START", MENUITEM_LABEL_LEN);
}
}
static void paint(struct MenuScene *self)
{
struct Priv *priv = self->extra;
print_labels(self->items);
LCD_clearDisplay(0);
LCD_setLine(XLINE-1, 0, XLINE-1, LCD_HEIGHT, BLACK);
char buf[10];
sprintf(buf, "%.0f", analog_read());
// TODO centering
LCD_setStrEx(buf, 0, 0, BLACK, (struct TextStyle) {.size = FONT_DOUBLE});
LCD_setStrEx("°C", 0, 17, BLACK, (struct TextStyle) {.size = FONT_BOLD});
if (fire_enabled()) {
strcpy(buf, "RUN");
} else {
strcpy(buf, "Off");
}
LCD_setStrEx(buf, 0, 30, BLACK, (struct TextStyle) {.size = FONT_BOLD});
priv->basePaint((struct Scene *) self);
}
struct Scene *NewScene_MenuTest() {
struct MenuItem *items = calloc(2, sizeof(struct MenuItem));
print_labels(items);
struct MenuScene * scene = NewScene_Menu(items, 2);
scene->onSelect = onSelect;
scene->x = XLINE;
scene->ncols = 8;
// private data added by the subclass
struct Priv *priv = calloc(1, sizeof(struct Priv));
priv->basePaint = scene->base.paint;
scene->base.paint = (Scene_paint_t) paint;
scene->extra = priv;
scene->extra_deinit = (Scene_deinit_t) priv_deinit;
// add a child return handler (base does not use this)
scene->base.onChildReturn = (Scene_onChildReturn_t) onChildReturn;
return (struct Scene *) scene;
}

@ -2,6 +2,8 @@
#include <graphics/bitmaps.h>
#include <sys/param.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <liquid/input_event.h>
#include "liquid.h"
@ -23,17 +25,29 @@ static void paint(struct MenuScene *self)
}
#endif
#define HSCROLL_START_MS 750
#define HSCROLL_MS 250
static void paint(struct MenuScene *self)
{
LCD_clearDisplay(0);
LCD_setRect(self->x, self->y, self->x + self->ncols*6, self->y + self->nlines*9, 1, WHITE);
int row = 0;
for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + self->visible_lines); i++, row++) {
for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + self->nlines); i++, row++) {
bool selected = self->selected==i;
if (selected) LCD_setRect(0, row*9, LCD_WIDTH-1, row*9 + 8, 1, 1);
LCD_setStrEx(self->items[i].label, 1, row * 9 + 1, !selected, (struct TextStyle) {
if (selected) LCD_setRect(self->x, self->y + row*9, self->x + self->ncols*6, self->y + row*9 + 8, 1, 1);
size_t shift = 0;
if (selected) {
size_t len = utf8_strlen(self->items[i].label);
shift = self->hscroll;
if (shift > len - self->ncols) {
shift = len - self->ncols;
}
}
LCD_setStrEx(self->items[i].label, self->x + 1, self->y + row * 9 + 1, !selected, (struct TextStyle) {
.bg = selected,
.max_chars = 13,
.nowrap = 1,
.skip = shift,
});
}
}
@ -54,10 +68,15 @@ static struct SceneEvent onInput(struct MenuScene *self, struct InputEvent event
self->scroll_up--;
}
while (self->selected - self->scroll_up >= (self->visible_lines - 1) && self->scroll_up < self->items_len - self->visible_lines) {
while (self->selected - self->scroll_up >= (self->nlines - 1) &&
self->scroll_up < self->items_len - self->nlines) {
self->scroll_up++;
}
// reset hscroll
self->hscroll_cnt = 0;
self->hscroll = 0;
return SceneEvent_Repaint();
case InputEventKind_Button:
@ -73,6 +92,35 @@ static struct SceneEvent onInput(struct MenuScene *self, struct InputEvent event
}
}
static struct SceneEvent onTick(struct MenuScene *self, uint32_t millis) {
size_t len = utf8_strlen(self->items[self->selected].label);
self->hscroll_cnt += millis;
if (len > self->ncols) {
size_t limit = (self->hscroll == 0 ? HSCROLL_START_MS : HSCROLL_MS);
if (self->hscroll_cnt >= limit) {
self->hscroll_cnt -= limit;
self->hscroll++;
if (self->hscroll > len - self->ncols + 2/* trailing delay */) {
self->hscroll = 0;
}
return SceneEvent_Repaint();
}
} else {
self->hscroll = 0;
// ensure periodic repaint (for parent)
if (self->hscroll_cnt > 20) {
return SceneEvent_Repaint();
}
}
return SceneEvent_None();
}
static void deinit(struct MenuScene *self)
{
free(self->items);
@ -90,14 +138,19 @@ struct MenuScene *NewScene_Menu(struct MenuItem *items, size_t items_len) {
scene->base.paint = (Scene_paint_t) paint;
scene->base.onInput = (Scene_onInput_t) onInput;
scene->base.deinit = (Scene_deinit_t) deinit;
scene->base.onTick = (Scene_onTick_t) onTick;
// onChildReturn is free to set by subclass
// TODO long selected label scrolling on tick
scene->visible_lines = 5;
scene->items = items;
scene->items_len = items_len;
scene->nlines = 5;
scene->ncols = 14;
scene->x = 0;
scene->y = 0;
return scene;
}

@ -40,12 +40,22 @@ struct MenuScene {
struct MenuItem *items;
// items count
size_t items_len;
// number of visible characters in a line at a time
uint8_t ncols;
// Vertical scroll offset (how many steps is the topmost item moved above the screen)
int32_t scroll_up;
// Start X
int x;
// Start Y
int y;
// Selected item number
int32_t selected;
// Horizontal scroll count
int32_t hscroll;
// Horizontal scroll timer
int32_t hscroll_cnt;
// Number of lines visible at a time
uint8_t visible_lines;
uint8_t nlines;
// selection handler
MenuScene_onSelect_t onSelect;
// Extra field for the subclass's private use.

@ -1,93 +0,0 @@
#include <malloc.h>
#include <graphics/bitmaps.h>
#include <sys/param.h>
#include <string.h>
#include <stdio.h>
#include "liquid.h"
#include "graphics/drawing.h"
#include "scenes.h"
#include "scene_menu.h"
struct Priv {
int32_t number;
};
static struct SceneEvent onSelect(struct MenuScene *self) {
struct Priv *priv = self->extra;
if (self->selected == 0) {
return SceneEvent_Close(0, NULL);
}
if (self->selected == 8) {
return SceneEvent_OpenChild(NewScene_Car(), 0);
}
if (self->selected == 9) {
return SceneEvent_OpenChild(NewScene_Demo(), 0);
}
if (self->selected == 2) {
struct NumberSceneOpts *opts = calloc(1, sizeof(struct NumberSceneOpts));
strcpy(opts->unit, "°C");
strcpy(opts->label, "Setpoint");
opts->value = priv->number;
opts->min = 0;
opts->max = 350;
return SceneEvent_OpenChild(NewScene_Number(opts), 99);
}
if (self->selected == 5) {
self->items[5].tag++;
snprintf(self->items[5].label, MENUITEM_LABEL_LEN, "Clicked %dx", self->items[5].tag);
return SceneEvent_Repaint();
}
return SceneEvent_None();
}
static void priv_deinit(struct MenuScene *self)
{
free(self->extra);
self->extra = NULL;
}
static struct SceneEvent onChildReturn(struct MenuScene *self, uint32_t tag, int32_t status, void *data)
{
struct Priv *priv = self->extra;
if (tag == 99) {
priv->number = status;
snprintf(self->items[2].label, MENUITEM_LABEL_LEN, "Set=%d°C", priv->number);
}
// this should be unreachable
return SceneEvent_None();
}
struct Scene *NewScene_MenuTest() {
struct MenuItem *items = calloc(10, sizeof(struct MenuItem));
strncpy(items[0].label, "🔙Back", MENUITEM_LABEL_LEN);
strncpy(items[1].label, "▶#1", MENUITEM_LABEL_LEN);
strncpy(items[2].label, "Set=0°C", MENUITEM_LABEL_LEN);
strncpy(items[3].label, "▶#3", MENUITEM_LABEL_LEN);
strncpy(items[4].label, "▶#4", MENUITEM_LABEL_LEN);
strncpy(items[5].label, "Clicked 0x", MENUITEM_LABEL_LEN);
strncpy(items[6].label, "▶#6", MENUITEM_LABEL_LEN);
strncpy(items[7].label, "▶#7", MENUITEM_LABEL_LEN);
strncpy(items[8].label, "Car", MENUITEM_LABEL_LEN);
strncpy(items[9].label, "Demo Scene", MENUITEM_LABEL_LEN);
struct MenuScene * scene = NewScene_Menu(items, 10);
scene->onSelect = onSelect;
// private data added by the subclass
struct Priv *priv = calloc(1, sizeof(struct Priv));
priv->number = 0;
scene->extra = priv;
scene->extra_deinit = (Scene_deinit_t) priv_deinit;
// add a child return handler (base does not use this)
scene->base.onChildReturn = (Scene_onChildReturn_t) onChildReturn;
return (struct Scene *) scene;
}

@ -11,8 +11,6 @@
struct Scene *NewScene_Root(void);
struct Scene *NewScene_Boot(void);
struct Scene *NewScene_Demo(void);
struct Scene *NewScene_Car(void);
struct Scene *NewScene_MenuTest(void);
#include "scene_menu.h"

Loading…
Cancel
Save