diff --git a/hecl/driver/ToolPackage.hpp b/hecl/driver/ToolPackage.hpp index 20001675a..1de8b04c4 100644 --- a/hecl/driver/ToolPackage.hpp +++ b/hecl/driver/ToolPackage.hpp @@ -53,13 +53,13 @@ class ToolPackage final : public ToolBase CheckFile(childPath); } - /* Directory with 2 components not "Shared" or macOS app buundle + /* Directory with 2 components not "Shared" or macOS app bundle * and no nested !world.blend files == General PAK */ if (checkGeneral && origSize == m_selectedItems.size()) { auto pathComps = path.getPathComponents(); if (pathComps.size() == 2 && pathComps[0] != _S("out") && - pathComps[1] != _S("Shared") && pathComps[0].find(".app") == hecl::SystemString::npos) + pathComps[1] != _S("Shared") && pathComps[0].find(_S(".app")) == hecl::SystemString::npos) AddSelectedItem(path); } } diff --git a/hecl/include/hecl/CVar.hpp b/hecl/include/hecl/CVar.hpp index f3427bc23..949c75678 100755 --- a/hecl/include/hecl/CVar.hpp +++ b/hecl/include/hecl/CVar.hpp @@ -78,6 +78,7 @@ public: std::string_view name() const { return m_name; } std::string_view rawHelp() const { return m_help; } std::string help() const; + std::string value() const { return m_value; } atVec4f toVec4f(bool* isValid = nullptr) const; float toFloat(bool* isValid = nullptr) const; @@ -92,6 +93,8 @@ public: bool fromInteger(int val); bool fromLiteral(std::string_view val); bool fromLiteral(std::wstring_view val); + bool fromLiteralToType(std::string_view val); + bool fromLiteralToType(std::wstring_view val); bool isFloat() const { return m_type == EType::Float; } bool isBoolean() const { return m_type == EType::Boolean; } @@ -132,7 +135,8 @@ private: std::string m_help; std::string m_defaultValue; EFlags m_flags; - bool m_allowedWrite; + EFlags m_oldFlags; + bool m_unlocked = false; CVarManager& m_mgr; diff --git a/hecl/include/hecl/Console.hpp b/hecl/include/hecl/Console.hpp index 2da4f9cf2..df96b86da 100644 --- a/hecl/include/hecl/Console.hpp +++ b/hecl/include/hecl/Console.hpp @@ -5,6 +5,7 @@ #include #include #include +#include "boo/IWindow.hpp" #include "logvisor/logvisor.hpp" namespace hecl @@ -49,17 +50,28 @@ public: Error, /**< Recoverable error message */ Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */ }; + + enum State + { + Closed, + Closing, + Opened, + Opening + }; + private: std::unordered_map m_commands; std::vector> m_log; - void visorReport(Level level, const char* mod, const char* fmt, va_list list); - void visorReport(Level level, const char* mod, const char* fmt, ...); - void visorReportSource(Level, const char* mod, const char* file, unsigned line, const char* fmt, va_list); - void visorReportSource(Level, const char* mod, const char* file, unsigned line, const char* fmt, ...); - void visorReport(Level level, const char* mod, const wchar_t* fmt, va_list list); - void visorReport(Level level, const char* mod, const wchar_t* fmt, ...); - void visorReportSource(Level, const char* mod, const char* file, unsigned line, const wchar_t* fmt, va_list); - void visorReportSource(Level, const char* mod, const char* file, unsigned line, const wchar_t* fmt, ...); + int m_logOffset; + std::string m_commandString; + std::vector m_commandHistory; + int m_cursorPosition = -1; + int m_currentCommand = -1; + int m_maxLines = 0; + bool m_overwrite : 1; + bool m_cursorAtEnd : 1; + State m_state = State::Closed; + public: Console(class CVarManager*); void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function&)>&& func); @@ -71,7 +83,13 @@ public: bool commandExists(std::string_view cmd); void report(Level level, const char *fmt, va_list list); - void report(Level, const char* fmt, ...); + void report(Level level, const char* fmt, ...); + + void proc(); + void draw(boo::IGraphicsCommandQueue* gfxQ); + void handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat); + void handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat); + void handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod); void dumpLog(); static Console* instance(); static void RegisterLogger(Console* con); diff --git a/hecl/lib/CVar.cpp b/hecl/lib/CVar.cpp index 7a3312684..05a83f7fe 100755 --- a/hecl/lib/CVar.cpp +++ b/hecl/lib/CVar.cpp @@ -21,14 +21,12 @@ CVar::CVar(std::string_view name, std::string_view value, std::string_view help, m_help = help; m_type = type; m_flags = flags; - m_allowedWrite = false; } CVar::CVar(std::string_view name, std::string_view value, std::string_view help, CVar::EFlags flags, CVarManager& parent) : m_mgr(parent) { m_flags = flags; - m_allowedWrite = false; m_name = std::string(name); m_help = help; m_type = EType::Literal; @@ -52,7 +50,6 @@ CVar::CVar(std::string_view name, const atVec4f& value, std::string_view help, E m_help = help; m_type = EType::Vec4f; m_flags = flags; - m_allowedWrite = false; // Unlock the cvar for writing if readonly unlock(); @@ -73,7 +70,6 @@ CVar::CVar(std::string_view name, float value, std::string_view help, EFlags fla m_help = help; m_type = EType::Float; m_flags = flags; - m_allowedWrite = false; // Unlock the cvar for writing if readonly unlock(); @@ -94,7 +90,6 @@ CVar::CVar(std::string_view name, bool value, std::string_view help, CVar::EFlag m_help = help; m_type = EType::Boolean; m_flags = flags; - m_allowedWrite = false; // Unlock the cvar for writing if readonly unlock(); @@ -115,7 +110,6 @@ CVar::CVar(std::string_view name, int value, std::string_view help, CVar::EFlags m_help = help; m_type = EType::Integer; m_flags = flags; - m_allowedWrite = false; // Unlock the cvar for writing if readonly unlock(); @@ -178,7 +172,7 @@ bool CVar::toBoolean(bool* isValid) const // We don't want to modify the original value; std::string tmp = m_value; - std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + athena::utility::tolower(tmp); if (!tmp.compare("yes") || !tmp.compare("true") || !tmp.compare("1")) { @@ -351,6 +345,84 @@ bool CVar::fromLiteral(std::wstring_view val) return true; } +bool CVar::fromLiteralToType(std::string_view val) +{ + switch (m_type) + { + case EType::Literal: return fromLiteral(val); + case EType::Boolean: + { + std::stringstream ss; + ss << std::boolalpha << val; + bool v; + ss >> v; + return fromBoolean(v); + } + case EType::Float: + { + std::stringstream ss; + ss << val; + float v; + ss >> v; + return fromFloat(v); + } + case EType::Integer: + { + std::stringstream ss; + ss << val; + int v; + ss >> v; + return fromInteger(v); + } + case EType::Vec4f: + { + atVec4f vec; + std::sscanf(val.data(), "%f %f %f %f", &vec.vec[0], &vec.vec[1], &vec.vec[2], &vec.vec[3]); + return fromVec4f(vec); + } + } + return false; +} + +bool CVar::fromLiteralToType(std::wstring_view val) +{ + switch (m_type) + { + case EType::Literal: return fromLiteral(val); + case EType::Boolean: + { + std::wstringstream ss; + ss << std::boolalpha << val; + bool v; + ss >> v; + return fromBoolean(v); + } + case EType::Float: + { + std::wstringstream ss; + ss << val; + float v; + ss >> v; + return fromFloat(v); + } + case EType::Integer: + { + std::wstringstream ss; + ss << val; + int v; + ss >> v; + return fromInteger(v); + } + case EType::Vec4f: + { + atVec4f vec; + std::swscanf(val.data(), L"%f %f %f %f", &vec.vec[0], &vec.vec[1], &vec.vec[2], &vec.vec[3]); + return fromVec4f(vec); + } + } + return false; +} + bool CVar::isModified() const { return int(m_flags & EFlags::Modified) != 0;} bool CVar::modificationRequiresRestart() const { return int(m_flags & EFlags::ModifyRestart) != 0; } @@ -362,32 +434,30 @@ bool CVar::isHidden() const { return int(m_flags & EFlags::Hidden) != 0; } bool CVar::isArchive() const { return int(m_flags & EFlags::Archive) != 0; } -void CVar::clearModified() { m_flags &= ~EFlags::Modified; } +void CVar::clearModified() +{ + if (!modificationRequiresRestart()) + m_flags &= ~EFlags::Modified; +} void CVar::setModified() { m_flags |= EFlags::Modified; } void CVar::unlock() { - if (!isReadOnly()) - return; - - if (!m_allowedWrite) + if (isReadOnly() && !m_unlocked) { - m_allowedWrite = true; + m_oldFlags = m_flags; m_flags &= ~EFlags::ReadOnly; + m_unlocked = true; } } void CVar::lock() { - if (!isReadOnly()) - return; - - if (m_allowedWrite) + if (!isReadOnly() && m_unlocked) { - m_flags |= EFlags::ReadOnly; - m_allowedWrite = false; - clearModified(); + m_flags = m_oldFlags; + m_unlocked = false; } } diff --git a/hecl/lib/CVarManager.cpp b/hecl/lib/CVarManager.cpp index 2f4e2c729..42a731a5a 100755 --- a/hecl/lib/CVarManager.cpp +++ b/hecl/lib/CVarManager.cpp @@ -129,10 +129,7 @@ void CVarManager::deserialize(CVar* cvar) } if (cvar->m_value != tmp.m_value) - { cvar->m_value = tmp.m_value; - cvar->m_flags |= CVar::EFlags::Modified; - } } } } @@ -178,16 +175,57 @@ CVarManager* CVarManager::instance() void CVarManager::list(Console* con, const std::vector &args) { - + for (const auto& cvar : m_cvars) + { + if (!cvar.second->isHidden()) + con->report(Console::Level::Info, "%s: %s", cvar.first.c_str(), cvar.second->help().c_str()); + } } void CVarManager::setCVar(Console* con, const std::vector &args) { + if (args.size() < 2) + { + con->report(Console::Level::Info, "Usage setCvar "); + return; + } + std::string cvName = args[0]; + athena::utility::tolower(cvName); + if (m_cvars.find(cvName) == m_cvars.end()) + { + con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str()); + return; + } + + CVar* cv = m_cvars[cvName]; + std::string value = args[1]; + auto it = args.begin() + 2; + for (; it != args.end(); ++it) + value += " " + *it; + + if (!cv->fromLiteralToType(value)) + con->report(Console::Level::Warning, "Unable to cvar '%s' to value '%s'", args[0].c_str(), value.c_str()); } void CVarManager::getCVar(Console* con, const std::vector &args) { + if (args.empty()) + { + con->report(Console::Level::Info, "Usage getCVar "); + return; + } + + std::string cvName = args[0]; + athena::utility::tolower(cvName); + if (m_cvars.find(cvName) == m_cvars.end()) + { + con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str()); + return; + } + + const CVar* cv = m_cvars[cvName]; + con->report(Console::Level::Info, "'%s' = '%s'", cv->name().data(), cv->value().c_str()); } bool CVarManager::restartRequired() const diff --git a/hecl/lib/Console.cpp b/hecl/lib/Console.cpp index fb221df8c..16bf8b09c 100644 --- a/hecl/lib/Console.cpp +++ b/hecl/lib/Console.cpp @@ -7,6 +7,8 @@ namespace hecl { Console* Console::m_instance = nullptr; Console::Console(CVarManager* cvarMgr) + : m_overwrite(false) + , m_cursorAtEnd(false) { m_instance = this; registerCommand("help", "Prints information about a given function", "", std::bind(&Console::help, this, std::placeholders::_1, std::placeholders::_2)); @@ -95,6 +97,7 @@ void Console::report(Level level, const char* fmt, va_list list) char tmp[2048]; vsnprintf(tmp, 2048, fmt, list); m_log.emplace_back(std::string(tmp), level); + printf("%s\n", tmp); } void Console::report(Level level, const char* fmt, ...) @@ -105,91 +108,256 @@ void Console::report(Level level, const char* fmt, ...) va_end(ap); } -void Console::visorReport(Console::Level level, const char* mod, const wchar_t* fmt, va_list list) +void Console::proc() { - wchar_t tmp[2048]; - vswprintf(tmp, 2048, fmt, list); - std::string v = athena::utility::sprintf("[%s] %s", mod, athena::utility::wideToUtf8(tmp).c_str()); - m_log.emplace_back(athena::utility::wideToUtf8(tmp), level); + if (m_state == State::Opened) + { + printf("\r%s ", m_commandString.c_str()); + fflush(stdout); + } + else if (m_state == State::Opening) + m_state = State::Opened; + else if (m_state == State::Closing) + m_state = State::Closed; + + + if (m_cursorPosition > (int)m_commandString.size() - 1) + m_cursorPosition = (int)m_commandString.size() - 1; + if (m_cursorPosition < -1) + m_cursorPosition = -1; + + if (m_logOffset > (int)m_log.size() - 1) + m_logOffset = (int)m_log.size() - 1; + if (m_logOffset < 0) + m_logOffset = 0; } -void Console::visorReport(Console::Level level, const char* mod, const wchar_t* fmt, ...) +void Console::draw(boo::IGraphicsCommandQueue* gfxQ) { - va_list ap; - va_start(ap, fmt); - report(level, mod, fmt, ap); - va_end(ap); } -void Console::visorReportSource(Console::Level level, const char* mod, const char* file, unsigned line, const char* fmt, ...) +void Console::handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat) { - va_list ap; - va_start(ap, fmt); - visorReportSource(level, mod, file, line, fmt, ap); - va_end(ap); + if (chr == U'`' || chr == U'~') + { + if (m_state == State::Closed || m_state == State::Closing) + m_state = State::Opening; + else + m_state = State::Closing; + } + + if (m_state == State::Opened) + { + if (!m_commandString.empty() && m_cursorPosition + 1 < m_commandString.size()) + { + if (m_overwrite) + m_commandString[m_cursorPosition + 1] = chr; + else + m_commandString.insert(m_commandString.begin() + m_cursorPosition + 1, chr); + } + else + m_commandString += chr; + + ++m_cursorPosition; + } } -void Console::visorReportSource(Console::Level level, const char *mod, const char *file, unsigned line, const wchar_t *fmt, va_list ap) +void Console::handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat) { - wchar_t tmp[2048]; - vswprintf(tmp, 2048, fmt, ap); - std::string v = athena::utility::sprintf("[%s] %s %s:%i", mod, athena::utility::wideToUtf8(tmp).c_str(), file, line); - m_log.emplace_back(v, level); + if (m_state != Opened) + return; + + switch (sp) + { + case boo::ESpecialKey::Insert: + m_overwrite ^= 1; + break; + case boo::ESpecialKey::Backspace: + { + if (!m_commandString.empty()) + { + if (int(mod & boo::EModifierKey::Ctrl) != 0) + { + int index = m_commandString.rfind(' ', m_cursorPosition - 1); + + if (index == (int)std::string::npos) + { + m_commandString.clear(); + m_cursorPosition = -1; + } + else + { + m_commandString.erase(index, (index - m_commandString.size())); + m_cursorPosition = index; + } + break; + } + if (m_cursorPosition < 0) + break; + + m_commandString.erase(m_cursorPosition, 1); + --m_cursorPosition; + } + break; + } + case boo::ESpecialKey::Delete: + { + if (!m_commandString.empty()) + { + // Don't try to delete if the cursor is at the end of the line + if ((m_cursorPosition + 1) >= (int)m_commandString.size()) + break; + + if (int(mod & boo::EModifierKey::Ctrl) != 0) + { + int index = m_commandString.find_first_of(' ', m_cursorPosition + 1); + if (index != std::string::npos) + m_commandString.erase(m_cursorPosition + 1, index + 1); + else + m_commandString.erase(m_cursorPosition + 1, (m_cursorPosition + 1) - m_commandString.size()); + break; + } + m_commandString.erase(m_cursorPosition + 1, 1); + } + break; + } + case boo::ESpecialKey::PgUp: + { + if (m_logOffset < (int)(m_log.size() - m_maxLines) - 1) + m_logOffset++; + break; + } + case boo::ESpecialKey::PgDown: + { + if (m_logOffset > 0) + m_logOffset--; + break; + } + case boo::ESpecialKey::Enter: + { + printf("\n"); + executeString(m_commandString); + m_cursorPosition = -1; + m_commandHistory.insert(m_commandHistory.begin(), m_commandString); + m_commandString.clear(); + //m_showCursor = true; + //m_cursorTime = 0; + break; + } + case boo::ESpecialKey::Left: + { + if (m_cursorPosition < 0) + break; + + if (int(mod & boo::EModifierKey::Ctrl) != 0) + m_cursorPosition = (int)m_commandString.rfind(' ', m_cursorPosition) - 1; + else + m_cursorPosition--; + + //m_showCursor = true; + //m_cursorTime = 0; + break; + } + case boo::ESpecialKey::Right: + { + if (m_cursorPosition >= (int)m_commandString.size() - 1) + break; + + if (int(mod & boo::EModifierKey::Ctrl) != 0) + { + if (m_commandString[m_cursorPosition] == ' ') + m_cursorPosition++; + + int tmpPos = m_commandString.find(' ', m_cursorPosition); + if (tmpPos == std::string::npos) + m_cursorPosition = m_commandString.size() - 1; + else + m_cursorPosition = tmpPos; + } + else + m_cursorPosition++; + +// m_showCursor = true; +// m_cursorTime = 0; + break; + } + + case boo::ESpecialKey::Up: + { + if (m_commandHistory.size() == 0) + break; + + m_currentCommand++; + + if (m_currentCommand > (int)m_commandHistory.size() - 1) + m_currentCommand = (int)m_commandHistory.size() - 1; + + m_commandString = m_commandHistory[m_currentCommand]; + m_cursorPosition = m_commandString.size(); + break; + } + case boo::ESpecialKey::Down: + { + if (m_commandHistory.empty()) + break; + m_currentCommand--; + if (m_currentCommand >= 0) + { + m_commandString = m_commandHistory[m_currentCommand]; + } + else if (m_currentCommand <= -1) + { + m_currentCommand = -1; + m_commandString.clear(); + } + m_cursorPosition = m_commandString.size(); + break; + } + case boo::ESpecialKey::Home: + m_cursorPosition = -1; + break; + case boo::ESpecialKey::End: + m_cursorPosition = m_commandString.size() - 1; + break; + default: + break; + } } -void Console::visorReportSource(Console::Level level, const char *mod, const char* file, unsigned line, const wchar_t* fmt, ...) +void Console::handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod) { - va_list ap; - va_start(ap, fmt); - visorReportSource(level, mod, file, line, fmt, ap); - va_end(ap); -} - -void Console::visorReport(Console::Level level, const char* mod, const char* fmt, va_list ap) -{ - char tmp[2048]; - vsnprintf(tmp, 2048, fmt, ap); - std::string v = athena::utility::sprintf("[%s] %s", mod, tmp); - m_log.emplace_back(v, level); -} - -void Console::visorReport(Console::Level level, const char* mod, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - report(level, mod, fmt, ap); - va_end(ap); -} - -void Console::visorReportSource(Console::Level level, const char* mod, const char* file, unsigned line, const char* fmt, va_list ap) -{ - char tmp[2048]; - vsnprintf(tmp, 2048, fmt, ap); - std::string v = athena::utility::sprintf("[%s] %s %s:%i", mod, tmp, file, line); - m_log.emplace_back(v, level); } void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const char *format, va_list ap) { - m_con->visorReport(Console::Level(severity), modName, format, ap); + char tmp[2048]; + vsnprintf(tmp, 2048, format, ap); + std::string v = athena::utility::sprintf("[%s] %s", modName, tmp); + m_con->m_log.emplace_back(v, Console::Level(severity)); } void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const wchar_t* format, va_list ap) { - m_con->visorReport(Console::Level(severity), modName, format, ap); + wchar_t tmp[2048]; + vswprintf(tmp, 2048, format, ap); + std::string v = athena::utility::sprintf("[%s] %s", modName, athena::utility::wideToUtf8(tmp).c_str()); + m_con->m_log.emplace_back(v, Console::Level(severity)); } void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, const char* format, va_list ap) { - m_con->visorReportSource(Console::Level(severity), modName, file, linenum, format, ap); + char tmp[2048]; + vsnprintf(tmp, 2048, format, ap); + std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, tmp, file, linenum); + m_con->m_log.emplace_back(v, Console::Level(severity)); } void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, const wchar_t* format, va_list ap) { wchar_t tmp[2048]; vswprintf(tmp, 2048, format, ap); - std::string v = athena::utility::wideToUtf8(tmp); - m_con->visorReportSource(Console::Level(severity), modName, file, linenum, format, ap); + std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, athena::utility::wideToUtf8(tmp).c_str(), file, linenum); + m_con->m_log.emplace_back(v, Console::Level(severity)); } void Console::dumpLog() diff --git a/hecl/test/main.cpp b/hecl/test/main.cpp index 2c73c252a..21ff16c14 100644 --- a/hecl/test/main.cpp +++ b/hecl/test/main.cpp @@ -1,14 +1,17 @@ #include #include "logvisor/logvisor.hpp" +#include "hecl/Console.hpp" +#include "hecl/CVarManager.hpp" #include #include "hecl/Runtime.hpp" #include "hecl/HMDLMeta.hpp" - #include #include #include #include +using namespace std::literals; + namespace hecl::Database { std::vector DATA_SPEC_REGISTRY; @@ -18,7 +21,7 @@ struct HECLWindowCallback : boo::IWindowCallback { bool m_sizeDirty = false; boo::SWindowRect m_latestSize; - void resized(const boo::SWindowRect& rect) + void resized(const boo::SWindowRect& rect, bool sync) { m_sizeDirty = true; m_latestSize = rect; @@ -29,14 +32,38 @@ struct HECLWindowCallback : boo::IWindowCallback { m_destroyed = true; } + + void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat) + { + hecl::Console::instance()->handleCharCode(charCode, mods, isRepeat); + } + void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat) + { + hecl::Console::instance()->handleSpecialKeyDown(key, mods, isRepeat); + } + void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods) + { + hecl::Console::instance()->hecl::Console::handleSpecialKeyUp(key, mods); + } }; struct HECLApplicationCallback : boo::IApplicationCallback { HECLWindowCallback m_windowCb; + hecl::Runtime::FileStoreManager m_fileStoreMgr; + hecl::CVarManager m_cvarManager; + hecl::Console m_console; std::shared_ptr m_mainWindow; bool m_running = true; + HECLApplicationCallback() + : m_fileStoreMgr("heclTest"), + m_cvarManager(m_fileStoreMgr), + m_console(&m_cvarManager) + { + m_console.registerCommand("quit"sv, "Quits application"sv, "", std::bind(&HECLApplicationCallback::quit, this, std::placeholders::_1, std::placeholders::_2)); + } + int appMain(boo::IApplication* app) { hecl::VerbosityLevel = 2; @@ -195,6 +222,8 @@ struct HECLApplicationCallback : boo::IApplicationCallback m_windowCb.m_sizeDirty = false; } + m_console.proc(); + gfxQ->setRenderTarget(renderTex); boo::SWindowRect r = m_windowCb.m_latestSize; r.location[0] = 0; @@ -212,6 +241,7 @@ struct HECLApplicationCallback : boo::IApplicationCallback gfxQ->setShaderDataBinding(binding); gfxQ->drawIndexed(0, 4); gfxQ->resolveDisplay(renderTex); + m_console.draw(gfxQ); gfxQ->execute(); ++frameIdx; @@ -228,6 +258,11 @@ struct HECLApplicationCallback : boo::IApplicationCallback { m_running = false; } + + void quit(hecl::Console* con, const std::vector& args) + { + m_running = false; + } }; void AthenaExcHandler(athena::error::Level level,