diff options
| author | hachem <im@hachem.wtf> | 2026-08-27 09:05:17 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-27 09:05:17 +0200 |
| commit | 4b64b2bd17433f427197af3679c9c8f5ad5ad1a4 (patch) | |
| tree | 7c22cd4fd1d4d3d1d69a8b992adae3e5e6f9e898 /src | |
| parent | bbe77bbbfbac0043a29d76817af0991996033f93 (diff) | |
add: application run loop + logging
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/application.cpp | 75 | ||||
| -rw-r--r-- | src/core/application.h | 48 | ||||
| -rw-r--r-- | src/core/assert.h | 20 | ||||
| -rw-r--r-- | src/core/log.h | 137 | ||||
| -rw-r--r-- | src/main.cpp | 17 |
5 files changed, 294 insertions, 3 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp new file mode 100644 index 0000000..fd04e62 --- /dev/null +++ b/src/core/application.cpp @@ -0,0 +1,75 @@ +#include "core/application.h" + +#include "core/assert.h" +#include "core/log.h" + +#include <chrono> + +namespace Hsdbg +{ + namespace + { + using Clock = std::chrono::steady_clock; + + constexpr float MAX_DELTA_TIME = 0.25f; + + // the run loop has nothing to block on until the window owns it, so it is + // time boxed for now to keep the app from spinning forever + constexpr float PLACEHOLDER_RUN_TIME = 1.0f; + } + + Application* Application::s_instance = nullptr; + + Application::Application(const ApplicationSpec& spec) + : m_spec(spec) + { + HSDBG_ASSERT(s_instance == nullptr, "an Application instance already exists"); + s_instance = this; + + Log::info("{}: starting up", m_spec.name); + } + + Application::~Application() + { + Log::info("{}: shutting down after {} frames", m_spec.name, m_frame_count); + + s_instance = nullptr; + } + + auto Application::get() -> Application& + { + HSDBG_ASSERT(s_instance != nullptr, "no Application instance has been created"); + return *s_instance; + } + + auto Application::run() -> void + { + m_running = true; + + auto last_frame_time = Clock::now(); + + while (m_running) + { + const auto now = Clock::now(); + const float elapsed = std::chrono::duration<float>(now - last_frame_time).count(); + last_frame_time = now; + + m_delta_time = elapsed < MAX_DELTA_TIME ? elapsed : MAX_DELTA_TIME; + m_time += m_delta_time; + m_frame_count += 1; + + update(); + } + } + + auto Application::close() -> void + { + m_running = false; + } + + auto Application::update() -> void + { + if (m_time >= PLACEHOLDER_RUN_TIME) + close(); + } +} diff --git a/src/core/application.h b/src/core/application.h new file mode 100644 index 0000000..1e4a08d --- /dev/null +++ b/src/core/application.h @@ -0,0 +1,48 @@ +#pragma once + +#include <cstdint> +#include <string> + +namespace Hsdbg +{ + struct ApplicationSpec + { + std::string name = "hsdbg"; + uint32_t width = 1600; + uint32_t height = 900; + }; + + class Application + { + public: + explicit Application(const ApplicationSpec& spec); + ~Application(); + + Application(const Application&) = delete; + Application(Application&&) = delete; + auto operator=(const Application&) -> Application& = delete; + auto operator=(Application&&) -> Application& = delete; + + static auto get() -> Application&; + + auto run() -> void; + auto close() -> void; + + auto spec() const -> const ApplicationSpec& { return m_spec; } + auto is_running() const -> bool { return m_running; } + auto delta_time() const -> float { return m_delta_time; } + auto time() const -> float { return m_time; } + auto frame_count() const -> uint64_t { return m_frame_count; } + + private: + auto update() -> void; + + static Application* s_instance; + + ApplicationSpec m_spec; + bool m_running = false; + float m_delta_time = 0.0f; + float m_time = 0.0f; + uint64_t m_frame_count = 0; + }; +} diff --git a/src/core/assert.h b/src/core/assert.h new file mode 100644 index 0000000..eee1c96 --- /dev/null +++ b/src/core/assert.h @@ -0,0 +1,20 @@ +#pragma once + +#include "core/log.h" + +#include <exception> + +#ifdef HSDBG_DIST + #define HSDBG_ASSERT(condition, message) ((void)0) +#else + #define HSDBG_ASSERT(condition, message) \ + do \ + { \ + if (!(condition)) \ + { \ + ::Hsdbg::Log::error("assertion failed: {}", #condition); \ + ::Hsdbg::Log::error(" {}:{}: {}", __FILE__, __LINE__, message); \ + std::terminate(); \ + } \ + } while (false) +#endif diff --git a/src/core/log.h b/src/core/log.h new file mode 100644 index 0000000..db7440b --- /dev/null +++ b/src/core/log.h @@ -0,0 +1,137 @@ +#pragma once + +#include <cstdint> +#include <format> +#include <iostream> +#include <ostream> +#include <print> +#include <string_view> +#include <utility> + +// there is no standard way to ask whether a stream is a terminal +#ifdef HSDBG_WINDOWS + #include <io.h> +#else + #include <unistd.h> +#endif + +namespace Hsdbg::Log +{ + enum class Level : uint8_t + { + Trace, + Debug, + Info, + Warn, + Error, + }; + + namespace Detail + { + inline Level g_min_level = +#ifdef HSDBG_DIST + Level::Info; +#else + Level::Trace; +#endif + + inline auto label(Level level) -> std::string_view + { + switch (level) + { + case Level::Trace: return "trace"; + case Level::Debug: return "debug"; + case Level::Info: return "info "; + case Level::Warn: return "warn "; + case Level::Error: return "error"; + } + + return "?????"; + } + + inline auto color(Level level) -> std::string_view + { + switch (level) + { + case Level::Trace: return "\x1b[90m"; + case Level::Debug: return "\x1b[36m"; + case Level::Info: return "\x1b[32m"; + case Level::Warn: return "\x1b[33m"; + case Level::Error: return "\x1b[31m"; + } + + return ""; + } + + inline auto is_terminal(bool to_stderr) -> bool + { + constexpr int STDOUT_DESCRIPTOR = 1; + constexpr int STDERR_DESCRIPTOR = 2; + + const int descriptor = to_stderr ? STDERR_DESCRIPTOR : STDOUT_DESCRIPTOR; + +#ifdef HSDBG_WINDOWS + return _isatty(descriptor) != 0; +#else + return isatty(descriptor) != 0; +#endif + } + + inline auto write(Level level, std::string_view message) -> void + { + if (level < g_min_level) + return; + + const bool to_stderr = level >= Level::Warn; + std::ostream& stream = to_stderr ? std::cerr : std::cout; + + if (is_terminal(to_stderr)) + std::println(stream, "{}{}\x1b[0m {}", color(level), label(level), message); + else + std::println(stream, "{} {}", label(level), message); + + // never let a crash swallow the last lines that explain it + stream.flush(); + } + } + + inline auto set_min_level(Level level) -> void + { + Detail::g_min_level = level; + } + + inline auto min_level() -> Level + { + return Detail::g_min_level; + } + + template <typename... Args> + auto trace(std::format_string<Args...> fmt, Args&&... args) -> void + { + Detail::write(Level::Trace, std::format(fmt, std::forward<Args>(args)...)); + } + + template <typename... Args> + auto debug(std::format_string<Args...> fmt, Args&&... args) -> void + { + Detail::write(Level::Debug, std::format(fmt, std::forward<Args>(args)...)); + } + + template <typename... Args> + auto info(std::format_string<Args...> fmt, Args&&... args) -> void + { + Detail::write(Level::Info, std::format(fmt, std::forward<Args>(args)...)); + } + + template <typename... Args> + auto warn(std::format_string<Args...> fmt, Args&&... args) -> void + { + Detail::write(Level::Warn, std::format(fmt, std::forward<Args>(args)...)); + } + + template <typename... Args> + auto error(std::format_string<Args...> fmt, Args&&... args) -> void + { + Detail::write(Level::Error, std::format(fmt, std::forward<Args>(args)...)); + } +} diff --git a/src/main.cpp b/src/main.cpp index d518fe6..bb72b0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,17 @@ -#include <print> +#include "core/application.h" -auto main() -> int +auto main(int argc, char** argv) -> int { - std::println("Hachem's stupid debugger"); + (void)argc; + (void)argv; + + Hsdbg::ApplicationSpec spec; + spec.name = "hsdbg"; + spec.width = 1600; + spec.height = 900; + + Hsdbg::Application app(spec); + app.run(); + + return 0; } |
