Compare commits

..

No commits in common. "ea876e243d6b9055047b4083ac8443192129dcdf" and "2848c8ad12dff42bb34c3896ce0c26400edafd8c" have entirely different histories.

18 changed files with 323 additions and 359 deletions

1
.gitignore vendored
View File

@ -2,4 +2,3 @@ bin/*
*.bin *.bin
.pio .pio
*.swp *.swp
Pipfile.lock

View File

@ -5,7 +5,6 @@ pipeline:
commands: commands:
- pip install -U platformio - pip install -U platformio
- pio run -e ${VARIANT} - pio run -e ${VARIANT}
- pio run -e ${VARIANT} --target buildfs
matrix: matrix:
VARIANT: VARIANT:
- bike - bike
@ -13,6 +12,9 @@ matrix:
- esp32 - esp32
- esp32_wifi - esp32_wifi
- esp32_bluetooth - esp32_bluetooth
- home_lighting-12f
- home_lighting_grb
- prototype
- esp8266_wifi - esp8266_wifi
- esp8266 - esp8266
- esp8266-12f - esp8266-12f

View File

@ -10,6 +10,6 @@
"scenes": { "scenes": {
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"], "Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],
"Flashlight": ["Flashlight"] "Flashlight": ["Flashlight"]
}, }
"surfaceMap": "default" "surfaceMap": "default"
} }

View File

@ -12,6 +12,6 @@
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"], "Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],
"Acid": ["Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"], "Acid": ["Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"],
"Flashlight": ["Flashlight"] "Flashlight": ["Flashlight"]
}, }
"surfaceMap": "default" "surfaceMap": "default"
} }

View File

@ -1,11 +0,0 @@
{
"version": 1,
"tasks": [
"Renderer",
"ConfigInput",
"ConfigDisplay",
"InputBlip"
],
"scenes": [],
"surfaceMap": "default"
}

View File

@ -11,7 +11,6 @@
[common_env_data] [common_env_data]
src_filter = "+<*> -<.git/> -<.svn/> -<platform/> -<inputs/>" src_filter = "+<*> -<.git/> -<.svn/> -<platform/> -<inputs/>"
lib_ldf_mode = chain+ lib_ldf_mode = chain+
extra_scripts = verify-configs.py
src_build_flags = src_build_flags =
-DRENDERBUG_VERSION=3 -DRENDERBUG_VERSION=3
-DRENDERBUG_LED_PIN=14 -DRENDERBUG_LED_PIN=14
@ -92,7 +91,6 @@ src_build_flags =
[env:esp32] [env:esp32]
extends = config_nocolor extends = config_nocolor
extra_scripts = verify-configs.py
platform = espressif32 platform = espressif32
board = featheresp32 board = featheresp32
framework = arduino framework = arduino
@ -172,15 +170,15 @@ lib_deps =
ESP8266WiFi ESP8266WiFi
${config_ota.lib_deps} ${config_ota.lib_deps}
#[env:home_lighting_grb] [env:home_lighting_grb]
#extends = env:esp8266_wifi config_u8display extends = env:home_lighting
#src_build_flags = src_build_flags =
# ${env:home_lighting.src_build_flags} ${env:home_lighting.src_build_flags}
# -DRENDERBUG_LED_PACKING=GRB -DRENDERBUG_LED_PACKING=GRB
#[env:home_lighting-12f] [env:home_lighting-12f]
#extends = env:esp8266_wifi config_u8display extends = env:home_lighting
#board = esp12e board = esp12e
;[env:photon] ;[env:photon]
;platform = particlephoton ;platform = particlephoton

View File

@ -80,18 +80,7 @@ ConfigService::onStart()
if (strlen(m_config.data.loadedProfile) == 0) { if (strlen(m_config.data.loadedProfile) == 0) {
strcpy(m_config.data.loadedProfile, "default"); strcpy(m_config.data.loadedProfile, "default");
} }
if (m_overrideProfile != nullptr) {
loadProfile(m_overrideProfile);
} else {
loadProfile(m_config.data.loadedProfile); loadProfile(m_config.data.loadedProfile);
}
}
void
ConfigService::overrideProfile(const char* profileName)
{
m_overrideProfile = profileName;
} }
void void

View File

@ -36,12 +36,10 @@ struct ConfigService: public Task {
void handleEvent(const InputEvent &evt) override; void handleEvent(const InputEvent &evt) override;
const CoordinateMapping* coordMap() const { return &m_jsonMap; } const CoordinateMapping* coordMap() const { return &m_jsonMap; }
const char* loadedProfile() const; const char* loadedProfile() const;
void overrideProfile(const char* profileName);
private: private:
HardwareConfig m_config; HardwareConfig m_config;
JsonCoordinateMapping m_jsonMap; JsonCoordinateMapping m_jsonMap;
const char* m_overrideProfile = nullptr;
void loadProfile(const char* name); void loadProfile(const char* name);
void loadMap(const String& mapName); void loadMap(const String& mapName);

View File

@ -1,56 +0,0 @@
#include "./SafeMode.h"
#include "./LogService.h"
#include "./Platform.h"
#include "./Static.h"
#include "./Config.h"
TaskFunc safeModeNag([]{
static uint8_t frame = 0;
static CRGB* leds = FastLED.leds();
EVERY_N_SECONDS(30) {
Log.fatal("I am running in safe mode!");
}
EVERY_N_MILLISECONDS(16) {
frame++;
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
leds[i] = CRGB(0, 0, 0);
}
for(int idx = 0; idx < 3; idx++) {
uint8_t length = beatsin8(5, 3, HardwareConfig::MAX_LED_NUM, 0, idx * 5);
for(int i = 0; i < length; i++) {
leds[i] += CRGB(scale8(5, beatsin8(5 + i * 7, 0, 255, 0, i*3)), 0, 0);
}
}
FastLED.show();
}
});
#ifdef CONFIG_WIFI
#include "platform/arduino/WiFiTask.h"
#endif // CONFIG_WIFI
#ifdef CONFIG_OTA
#include "platform/arduino/OTA.h"
#endif // CONFIG_OTA
#ifdef CONFIG_MQTT
#include "platform/arduino/MQTTTelemetry.h"
#endif // CONFIG_MQTT
MainLoop
SafeMode::safeModeApp{{
Static<Platform>::instance(),
// System logging
Static<LogService>::instance(),
&safeModeNag,
#ifdef CONFIG_WIFI
// ESP Wifi
Static<WiFiTask>::instance(),
#endif // CONFIG_WIFI
#ifdef CONFIG_MQTT
// MQTT
Static<MQTTTelemetry>::instance(),
#endif // CONFIG_MQTT
#ifdef CONFIG_OTA
// OTA Updates
Static<ArduinoOTAUpdater>::instance(),
#endif // CONFIG_OTA
}};

View File

@ -1,7 +0,0 @@
#pragma once
#include <Figments.h>
class SafeMode {
public:
static MainLoop safeModeApp;
};

View File

@ -1,5 +0,0 @@
#include "./BPM.h"
#include "../Static.h"
STATIC_ALLOC(BPM);
STATIC_TASK(BPM);

View File

@ -1,52 +0,0 @@
#pragma once
#include <Figments.h>
class BPM : public InputSource {
public:
BPM() : InputSource("BPM") {}
void handleEvent(const InputEvent& evt) override {
if (evt.intent == InputEvent::BeatDetect) {
m_nextBpm = millis();
m_timings.insert(millis());
Log.notice("%d timings", m_timings.size());
if (m_timings.size() >= 5) {
updateBPM();
}
}
}
InputEvent read() override {
if (m_bpm > 0) {
uint16_t now = millis();
if (now >= m_nextBpm) {
m_nextBpm += m_bpm;
return InputEvent{InputEvent::Beat, m_bpm};
}
if (now >= m_nextLearn && m_nextLearn != 0) {
m_timings.clear();
m_nextLearn = 0;
}
}
return InputEvent{};
}
private:
uint16_t m_bpm = 0;
uint16_t m_nextBpm = 0;
uint16_t m_nextLearn = 0;
Ringbuf<uint16_t, 7> m_timings;
void updateBPM() {
uint16_t avgDelta = 0;
for(uint8_t i = 0; i < m_timings.size() - 1; i++) {
uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i);
Log.notice("Timing %d Delta %d", m_timings.peek(i), delta);
avgDelta += delta;
}
m_bpm = avgDelta / 4;
m_nextLearn = m_bpm * 5 + millis();
Log.notice("BPM is now %d", m_bpm);
uint16_t trash;
m_timings.take(trash);
}
};

View File

@ -1,5 +0,0 @@
#include "./CircadianRhythm.h"
#include "../Static.h"
STATIC_ALLOC(CircadianRhythm);
STATIC_TASK(CircadianRhythm);

View File

@ -1,90 +0,0 @@
#pragma once
#include <Figments.h>
struct ScheduleEntry {
uint8_t hour;
uint8_t brightness;
};
std::array<ScheduleEntry, 10> schedule{{
{0, 0},
{5, 0},
{6, 0},
{7, 10},
{8, 80},
{11, 120},
{18, 200},
{19, 255},
{22, 120},
{23, 20}
}};
class CircadianRhythm : public InputSource {
private:
bool needsUpdate = true;
public:
CircadianRhythm() : InputSource("CircadianRhythm") {}
void onStart() {
needsUpdate = true;
}
uint8_t brightnessForTime(uint8_t hour, uint8_t minute) const {
ScheduleEntry start = schedule.back();
ScheduleEntry end = schedule.front();
for(ScheduleEntry cur : schedule) {
// Find the last hour that is prior to or equal to now
if (cur.hour <= hour) {
start = cur;
} else {
break;
}
}
for(ScheduleEntry cur : schedule) {
// Find the first hour that is after now
// If no such hour exists, we should automatically wrap back to hour 0
if (cur.hour > hour) {
end = cur;
break;
}
}
if (start.hour > end.hour) {
end.hour += 24;
}
uint16_t startTime = start.hour * 60;
uint16_t endTime = end.hour * 60;
uint16_t nowTime = hour * 60 + minute;
uint16_t duration = endTime - startTime;
uint16_t curDuration = nowTime - startTime;
uint8_t frac = map8(curDuration, 0, duration);
return lerp8by8(start.brightness, end.brightness, frac);
}
InputEvent read() {
EVERY_N_SECONDS(60) {
needsUpdate = true;
}
if (needsUpdate) {
uint8_t hour = 0;
uint8_t minute = 0;
needsUpdate = false;
struct tm timeinfo;
if (Platform::getLocalTime(&timeinfo)) {
hour = timeinfo.tm_hour;
minute = timeinfo.tm_min;
} else {
hour = 0;
minute = 0;
}
Log.notice("Current time: %d:%d", hour, minute);
return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)};
}
return InputEvent{};
}
};

View File

@ -1,5 +0,0 @@
#include "./ConfigInput.h"
#include "./Static.h"
STATIC_ALLOC(ConfigInput);
STATIC_TASK(ConfigInput);

View File

@ -1,77 +0,0 @@
#pragma once
#include <Figments.h>
class ConfigInputTask : public BufferedInputSource {
public:
ConfigInputTask() : BufferedInputSource("ConfigInput") {}
void handleEvent(const InputEvent& evt) override {
if (evt.intent == InputEvent::UserInput) {
Buttons::Chord chord = (Buttons::Chord) evt.asInt();
switch (chord) {
case Buttons::Circle:
m_currentIntent = nextIntent();
//Log.info("Next setting... (%d)", m_currentIntent);
break;
case Buttons::CircleTriangle:
//Log.info("Increment...");
increment();
break;
case Buttons::CircleCross:
//Log.info("Decrement...");
decrement();
break;
case Buttons::Triangle:
//Log.info("Save...");
setEvent(InputEvent::SaveConfigurationRequest);
break;
default:
break;
}
}
}
private:
InputEvent::Intent m_currentIntent = InputEvent::SetDisplayLength;
void decrement() {
int current = 0;
switch (m_currentIntent) {
case InputEvent::SetDisplayLength:
current = Static<ConfigService>::instance()->coordMap()->pixelCount;
break;
case InputEvent::SetDisplayOffset:
current = Static<ConfigService>::instance()->coordMap()->startPixel;
break;
default:
break;
}
setEvent(InputEvent{m_currentIntent, current - 1});
}
void increment() {
int current = 0;
switch (m_currentIntent) {
case InputEvent::SetDisplayLength:
current = Static<ConfigService>::instance()->coordMap()->pixelCount;
break;
case InputEvent::SetDisplayOffset:
current = Static<ConfigService>::instance()->coordMap()->startPixel;
break;
default:
break;
}
setEvent(InputEvent{m_currentIntent, current + 1});
}
InputEvent::Intent nextIntent() {
switch (m_currentIntent) {
case InputEvent::SetDisplayLength:
return InputEvent::SetDisplayOffset;
case InputEvent::SetDisplayOffset:
return InputEvent::SetDisplayLength;
default:
return InputEvent::None;
}
}
};

View File

@ -14,10 +14,11 @@
#include <time.h> #include <time.h>
#include "animations/Power.h" #include "animations/Power.h"
#include "animations/Drain.h"
#include "animations/InputBlip.h"
#include "inputs/ColorCycle.h" #include "inputs/ColorCycle.h"
#include "inputs/Buttons.h" #include "inputs/Buttons.h"
#include "SafeMode.h"
#define MAX_BRIGHTNESS 255 #define MAX_BRIGHTNESS 255
//#define PSU_MILLIAMPS 4800 //#define PSU_MILLIAMPS 4800
@ -35,9 +36,9 @@ Display dpy(leds, HardwareConfig::MAX_LED_NUM, Static<ConfigService>::instance()
// Setup power management // Setup power management
Power<MAX_BRIGHTNESS, PSU_MILLIAMPS> power; Power<MAX_BRIGHTNESS, PSU_MILLIAMPS> power;
REGISTER_TASK(power); REGISTER_TASK(power);
// FIXME: rewrite as static task
/*FigmentFunc configDisplay([](Display* dpy) { /*FigmentFunc configDisplay([](Display* dpy) {
uint8_t brightness = brighten8_video(beatsin8(60)); uint8_t brightness = brighten8_video(beatsin8(60));
auto coords = Static<ConfigService>::instance()->coordMap(); auto coords = Static<ConfigService>::instance()->coordMap();
@ -90,6 +91,63 @@ InputMapper keyMap([](const InputEvent& evt) {
REGISTER_TASK(keyMap); REGISTER_TASK(keyMap);
class BPM : public InputSource {
public:
BPM() : InputSource("BPM") {}
void handleEvent(const InputEvent& evt) override {
if (evt.intent == InputEvent::BeatDetect) {
m_nextBpm = millis();
m_timings.insert(millis());
Log.notice("%d timings", m_timings.size());
if (m_timings.size() >= 5) {
updateBPM();
}
}
}
InputEvent read() override {
if (m_bpm > 0) {
uint16_t now = millis();
if (now >= m_nextBpm) {
m_nextBpm += m_bpm;
return InputEvent{InputEvent::Beat, m_bpm};
}
if (now >= m_nextLearn && m_nextLearn != 0) {
m_timings.clear();
m_nextLearn = 0;
}
}
return InputEvent{};
}
private:
uint16_t m_bpm = 0;
uint16_t m_nextBpm = 0;
uint16_t m_nextLearn = 0;
Ringbuf<uint16_t, 7> m_timings;
void updateBPM() {
uint16_t avgDelta = 0;
for(uint8_t i = 0; i < m_timings.size() - 1; i++) {
uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i);
Log.notice("Timing %d Delta %d", m_timings.peek(i), delta);
avgDelta += delta;
}
m_bpm = avgDelta / 4;
m_nextLearn = m_bpm * 5 + millis();
Log.notice("BPM is now %d", m_bpm);
uint16_t trash;
m_timings.take(trash);
}
};
STATIC_ALLOC(BPM);
STATIC_TASK(BPM);
Renderer configRenderer{
{&dpy},
{Static<DrainAnimation>::instance(), /*&configDisplay,*/ Static<InputBlip>::instance(), &power}
};
// Cycle some random colors // Cycle some random colors
ColorSequenceInput<9> idleCycle{{ ColorSequenceInput<9> idleCycle{{
@ -113,7 +171,249 @@ ColorSequenceInput<7> rainbowCycle{{
REGISTER_TASK(rainbowCycle); REGISTER_TASK(rainbowCycle);
MainLoop* runner = &SafeMode::safeModeApp; /*struct ConfigInputTask: public BufferedInputSource {
public:
ConfigInputTask() : BufferedInputSource("ConfigInput") {}
void handleEvent(const InputEvent& evt) override {
if (evt.intent == InputEvent::UserInput) {
Buttons::Chord chord = (Buttons::Chord) evt.asInt();
switch (chord) {
case Buttons::Circle:
m_currentIntent = nextIntent();
//Log.info("Next setting... (%d)", m_currentIntent);
break;
case Buttons::CircleTriangle:
//Log.info("Increment...");
increment();
break;
case Buttons::CircleCross:
//Log.info("Decrement...");
decrement();
break;
case Buttons::Triangle:
//Log.info("Save...");
setEvent(InputEvent::SaveConfigurationRequest);
break;
default:
break;
}
}
}
private:
InputEvent::Intent m_currentIntent = InputEvent::SetDisplayLength;
void decrement() {
int current = 0;
switch (m_currentIntent) {
case InputEvent::SetDisplayLength:
current = Static<ConfigService>::instance()->coordMap()->pixelCount;
break;
case InputEvent::SetDisplayOffset:
current = Static<ConfigService>::instance()->coordMap()->startPixel;
break;
default:
break;
}
setEvent(InputEvent{m_currentIntent, current - 1});
}
void increment() {
int current = 0;
switch (m_currentIntent) {
case InputEvent::SetDisplayLength:
current = Static<ConfigService>::instance()->coordMap()->pixelCount;
break;
case InputEvent::SetDisplayOffset:
current = Static<ConfigService>::instance()->coordMap()->startPixel;
break;
default:
break;
}
setEvent(InputEvent{m_currentIntent, current + 1});
}
InputEvent::Intent nextIntent() {
switch (m_currentIntent) {
case InputEvent::SetDisplayLength:
return InputEvent::SetDisplayOffset;
case InputEvent::SetDisplayOffset:
return InputEvent::SetDisplayLength;
default:
return InputEvent::None;
}
}
};*/
struct ScheduleEntry {
uint8_t hour;
uint8_t brightness;
};
std::array<ScheduleEntry, 10> schedule{{
{0, 0},
{5, 0},
{6, 0},
{7, 10},
{8, 80},
{11, 120},
{18, 200},
{19, 255},
{22, 120},
{23, 20}
}};
class CircadianRhythm : public InputSource {
private:
bool needsUpdate = true;
public:
CircadianRhythm() : InputSource("CircadianRhythm") {}
void onStart() {
needsUpdate = true;
}
uint8_t brightnessForTime(uint8_t hour, uint8_t minute) const {
ScheduleEntry start = schedule.back();
ScheduleEntry end = schedule.front();
for(ScheduleEntry cur : schedule) {
// Find the last hour that is prior to or equal to now
if (cur.hour <= hour) {
start = cur;
} else {
break;
}
}
for(ScheduleEntry cur : schedule) {
// Find the first hour that is after now
// If no such hour exists, we should automatically wrap back to hour 0
if (cur.hour > hour) {
end = cur;
break;
}
}
if (start.hour > end.hour) {
end.hour += 24;
}
uint16_t startTime = start.hour * 60;
uint16_t endTime = end.hour * 60;
uint16_t nowTime = hour * 60 + minute;
uint16_t duration = endTime - startTime;
uint16_t curDuration = nowTime - startTime;
uint8_t frac = map8(curDuration, 0, duration);
return lerp8by8(start.brightness, end.brightness, frac);
}
InputEvent read() {
EVERY_N_SECONDS(60) {
needsUpdate = true;
}
if (needsUpdate) {
uint8_t hour = 0;
uint8_t minute = 0;
needsUpdate = false;
struct tm timeinfo;
if (Platform::getLocalTime(&timeinfo)) {
hour = timeinfo.tm_hour;
minute = timeinfo.tm_min;
} else {
hour = 0;
minute = 0;
}
Log.notice("Current time: %d:%d", hour, minute);
return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)};
}
return InputEvent{};
}
};
STATIC_ALLOC(CircadianRhythm);
STATIC_TASK(CircadianRhythm);
// A special mainloop app for configuring hardware settings that reboots the
// device when the user is finished.
/*MainLoop configApp{{
Static<Platform>::instance(),
// Manage read/write of configuration data
Static<ConfigService>::instance(),
// Read hardware inputs
Static<Buttons>::instance(),
// Map input buttons to configuration commands
new ConfigInputTask(),
// System logging
Static<LogService>::instance(),
// Fill the entire display with a color, to see size
&configDisplay,
// Render some basic input feedback
&inputBlip,
// Render it all
&configRenderer,
}};*/
MainLoop configApp{std::vector<Task*>()};
TaskFunc safeModeNag([]{
static uint8_t frame = 0;
EVERY_N_SECONDS(30) {
Log.fatal("I am running in safe mode!");
}
EVERY_N_MILLISECONDS(16) {
frame++;
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
leds[i] = CRGB(0, 0, 0);
}
for(int idx = 0; idx < 3; idx++) {
uint8_t length = beatsin8(5, 3, HardwareConfig::MAX_LED_NUM, 0, idx * 5);
for(int i = 0; i < length; i++) {
leds[i] += CRGB(scale8(5, beatsin8(5 + i * 7, 0, 255, 0, i*3)), 0, 0);
}
}
FastLED.show();
}
});
#ifdef CONFIG_WIFI
#include "platform/arduino/WiFiTask.h"
#endif // CONFIG_WIFI
#ifdef CONFIG_OTA
#include "platform/arduino/OTA.h"
#endif // CONFIG_OTA
#ifdef CONFIG_MQTT
#include "platform/arduino/MQTTTelemetry.h"
#endif // CONFIG_MQTT
MainLoop safeModeApp{{
Static<Platform>::instance(),
// System logging
Static<LogService>::instance(),
&safeModeNag,
#ifdef CONFIG_WIFI
// ESP Wifi
Static<WiFiTask>::instance(),
#endif // CONFIG_WIFI
#ifdef CONFIG_MQTT
// MQTT
Static<MQTTTelemetry>::instance(),
#endif // CONFIG_MQTT
#ifdef CONFIG_OTA
// OTA Updates
Static<ArduinoOTAUpdater>::instance(),
#endif // CONFIG_OTA
}};
MainLoop* runner = &safeModeApp;
void setup() { void setup() {
// Turn on, // Turn on,
@ -136,19 +436,18 @@ void setup() {
// Tune in, // Tune in,
if (Platform::bootopts.isSafeMode) { if (Platform::bootopts.isSafeMode) {
Log.notice(u8"⚠️ Starting Figment in safe mode!!!"); Log.notice(u8"⚠️ Starting Figment in safe mode!!!");
runner = &SafeMode::safeModeApp; runner = &safeModeApp;
FastLED.showColor(CRGB(5, 0, 0)); FastLED.showColor(CRGB(5, 0, 0));
FastLED.show(); FastLED.show();
} else if (Platform::bootopts.isSetup) {
Log.notice(u8"🔧 Starting Figment in configuration mode...");
FastLED.showColor(CRGB(0, 5, 0));
//runner = &configApp;
} else { } else {
Log.notice(u8"🌌 Starting Figment..."); Log.notice(u8"🌌 Starting Figment...");
if (Platform::bootopts.isSetup) {
Log.notice(u8"🔧 Booting up into setup profile!!!");
Static<ConfigService>::instance()->overrideProfile("setup");
}
// Render all layers to the displays // Render all layers to the displays
Renderer* renderer = new Renderer({&dpy}, std::vector<Figment*>{Platform::beginFigments(), Platform::endFigments()}); Renderer* renderer = new Renderer({&dpy}, std::vector<Figment*>{Platform::beginFigments(), Platform::endFigments()});
std::vector<Task*> defaultTasks{Platform::beginTasks(), Platform::endTasks()}; std::vector<Task*> defaultTasks{Platform::beginTasks(), Platform::endTasks()};
defaultTasks.push_back(renderer); defaultTasks.push_back(renderer);
runner = new MainLoop{std::vector<Task*>{defaultTasks.begin(), defaultTasks.end()}}; runner = new MainLoop{std::vector<Task*>{defaultTasks.begin(), defaultTasks.end()}};

View File

@ -1,13 +0,0 @@
Import("env")
import fnmatch
import os
def verify_json(source, target, env):
for root, dirnames, files in os.walk("data"):
for file in files:
if file.endswith(".json"):
if (env.Execute("json_verify < " + root + '/' + file)):
Exit(1)
env.AddPreAction("buildfs", verify_json)