hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2025-08-12 16:35:42 +0200
committerhachem <im@hachem.wtf>2025-08-12 16:35:42 +0200
commitdbe642e5e26cf2bd99218f727157b58c7fed286e (patch)
treeb964424747bdd0b143a4dfac894aee61fd752302 /src/core
parentd6b9f6c8fad3e46caca5dc167ce1cbf5ccfb489e (diff)
feat: application manager and EventDispatcher
Diffstat (limited to 'src/core')
-rw-r--r--src/core/application.cpp86
-rw-r--r--src/core/application.h35
-rw-r--r--src/core/event.h338
-rw-r--r--src/core/window.cpp157
-rw-r--r--src/core/window.h32
5 files changed, 641 insertions, 7 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp
new file mode 100644
index 0000000..d98ae4f
--- /dev/null
+++ b/src/core/application.cpp
@@ -0,0 +1,86 @@
+#include "application.h"
+#include <iostream>
+
+namespace Donut
+{
+ Application* Application::s_instance = nullptr;
+
+ Application::Application(const std::string& name, int width, int height)
+ : m_running(true), m_minimized(false)
+ {
+ s_instance = this;
+
+ m_window = std::make_unique<Window>(name, width, height);
+ m_window->set_event_callback([this](Event& event)
+ {
+ on_event(event);
+ });
+
+ on_init();
+ }
+
+ Application::~Application()
+ {
+ on_shutdown();
+ s_instance = nullptr;
+ }
+
+ void Application::run()
+ {
+ while (m_running)
+ {
+ if (!m_minimized)
+ {
+ on_update();
+ on_render();
+ }
+
+ m_window->on_update();
+ }
+ }
+
+ void Application::close()
+ {
+ m_running = false;
+ }
+
+ void Application::on_event(Event& event)
+ {
+ EventDispatcher dispatcher(event);
+ dispatcher.dispatch<WindowCloseEvent>([this, &event](WindowCloseEvent& e)
+ {
+ m_running = false;
+ event.handled = true;
+ return true;
+ });
+
+ dispatcher.dispatch<WindowResizeEvent>([this, &event](WindowResizeEvent& e)
+ {
+ if (e.get_width() == 0 || e.get_height() == 0)
+ m_minimized = true;
+ else
+ m_minimized = false;
+
+ event.handled = true;
+ return true;
+ });
+
+ dispatcher.dispatch<KeyPressedEvent>([this, &event](KeyPressedEvent& e)
+ {
+ if (e.get_key_code() == 256)
+ close();
+ return true;
+ });
+ }
+
+ void Application::on_init() { }
+ void Application::on_shutdown() { }
+
+ void Application::on_update()
+ {
+ }
+
+ void Application::on_render()
+ {
+ }
+}
diff --git a/src/core/application.h b/src/core/application.h
new file mode 100644
index 0000000..d88eab2
--- /dev/null
+++ b/src/core/application.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "window.h"
+#include "event.h"
+#include <memory>
+#include <functional>
+
+namespace Donut
+{
+ class Application
+ {
+ public:
+ Application(const std::string& name = "Donut",
+ int width = 1280, int height = 720);
+ ~Application();
+
+ void run();
+ void close();
+
+ Window& get_window() { return *m_window; }
+ static Application& get() { return *s_instance; }
+ private:
+ void on_init();
+ void on_shutdown();
+ void on_update();
+ void on_render();
+ void on_event(Event& event);
+ private:
+ std::unique_ptr<Window> m_window;
+ bool m_running;
+ bool m_minimized;
+
+ static Application* s_instance;
+ };
+}
diff --git a/src/core/event.h b/src/core/event.h
new file mode 100644
index 0000000..6639f28
--- /dev/null
+++ b/src/core/event.h
@@ -0,0 +1,338 @@
+#pragma once
+
+#include <functional>
+#include <unordered_map>
+#include <vector>
+#include <string>
+#include <sstream>
+
+namespace Donut
+{
+ class Event;
+ class EventDispatcher;
+
+ enum class EventType
+ {
+ None = 0,
+
+ WindowClose,
+ WindowResize,
+ WindowFocus,
+ WindowLostFocus,
+ WindowMoved,
+
+ KeyPressed,
+ KeyReleased,
+ KeyTyped,
+
+ MouseButtonPressed,
+ MouseButtonReleased,
+ MouseMoved,
+ MouseScrolled
+ };
+
+ enum EventCategory
+ {
+ None = 0,
+ EventCategoryApplication = 1 << 0,
+ EventCategoryInput = 1 << 1,
+ EventCategoryKeyboard = 1 << 2,
+ EventCategoryMouse = 1 << 3,
+ EventCategoryMouseButton = 1 << 4
+ };
+
+ class Event
+ {
+ public:
+ virtual ~Event() = default;
+
+ bool handled = false;
+
+ virtual EventType get_event_type() const = 0;
+ virtual const char* get_name() const = 0;
+ virtual int get_category_flags() const = 0;
+ virtual std::string to_string() const { return get_name(); }
+
+ bool is_in_category(EventCategory category) { return get_category_flags() & category; }
+ };
+
+ #define EVENT_CLASS_TYPE(type) \
+ static EventType get_static_type() { return EventType::type; } \
+ virtual EventType get_event_type() const override { return get_static_type(); } \
+ virtual const char* get_name() const override { return #type; }
+
+ #define APPLICATION_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual int get_category_flags() const override { return EventCategoryApplication; }
+
+ #define MOUSE_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual int get_category_flags() const override { return EventCategoryMouse | \
+ EventCategoryInput; }
+
+ #define MOUSE_BUTTON_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual int get_category_flags() const override { return EventCategoryMouseButton | \
+ EventCategoryMouse | \
+ EventCategoryInput; }
+
+ #define KEYBOARD_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual int get_category_flags() const override { return EventCategoryKeyboard | \
+ EventCategoryInput; }
+
+ class WindowResizeEvent : public Event
+ {
+ public:
+ WindowResizeEvent(unsigned int width, unsigned int height)
+ : m_width(width), m_height(height) { }
+
+ unsigned int get_width() const { return m_width; }
+ unsigned int get_height() const { return m_height; }
+
+ std::string to_string() const override
+ {
+ return "WindowResizeEvent: " + std::to_string(m_width) + ", " + std::to_string(m_height);
+ }
+
+ APPLICATION_EVENT_CLASS_TYPE(WindowResize)
+
+ private:
+ unsigned int m_width, m_height;
+ };
+
+ class WindowCloseEvent : public Event
+ {
+ public:
+ WindowCloseEvent() = default;
+ APPLICATION_EVENT_CLASS_TYPE(WindowClose)
+ };
+
+ class WindowFocusEvent : public Event
+ {
+ public:
+ WindowFocusEvent() = default;
+ APPLICATION_EVENT_CLASS_TYPE(WindowFocus)
+ };
+
+ class WindowLostFocusEvent : public Event
+ {
+ public:
+ WindowLostFocusEvent() = default;
+ APPLICATION_EVENT_CLASS_TYPE(WindowLostFocus)
+ };
+
+ class WindowMovedEvent : public Event
+ {
+ public:
+ WindowMovedEvent(int x, int y)
+ : m_x(x), m_y(y) { }
+
+ int get_x() const { return m_x; }
+ int get_y() const { return m_y; }
+
+ std::string to_string() const override
+ {
+ return "WindowMovedEvent: " + std::to_string(m_x) + ", " + std::to_string(m_y);
+ }
+
+ APPLICATION_EVENT_CLASS_TYPE(WindowMoved)
+
+ private:
+ int m_x, m_y;
+ };
+
+ class KeyEvent : public Event
+ {
+ public:
+ int get_key_code() const { return m_key_code; }
+ virtual int get_category_flags() const override { return EventCategoryKeyboard |
+ EventCategoryInput; }
+
+ protected:
+ KeyEvent(int keycode) : m_key_code(keycode) { }
+ int m_key_code;
+ };
+
+ class KeyPressedEvent : public KeyEvent
+ {
+ public:
+ KeyPressedEvent(int keycode, bool is_repeat = false)
+ : KeyEvent(keycode), m_is_repeat(is_repeat) { }
+
+ bool is_repeat() const { return m_is_repeat; }
+
+ std::string to_string() const override
+ {
+ return "KeyPressedEvent: " + std::to_string(m_key_code) +
+ " (repeat = " + std::to_string(m_is_repeat) + ")";
+ }
+
+ KEYBOARD_EVENT_CLASS_TYPE(KeyPressed)
+
+ private:
+ bool m_is_repeat;
+ };
+
+ class KeyReleasedEvent : public KeyEvent
+ {
+ public:
+ KeyReleasedEvent(int keycode) : KeyEvent(keycode) { }
+
+ std::string to_string() const override
+ {
+ return "KeyReleasedEvent: " + std::to_string(m_key_code);
+ }
+
+ KEYBOARD_EVENT_CLASS_TYPE(KeyReleased)
+ };
+
+ class KeyTypedEvent : public KeyEvent
+ {
+ public:
+ KeyTypedEvent(int keycode) : KeyEvent(keycode) { }
+
+ std::string to_string() const override
+ {
+ return "KeyTypedEvent: " + std::to_string(m_key_code);
+ }
+
+ KEYBOARD_EVENT_CLASS_TYPE(KeyTyped)
+ };
+
+ class MouseMovedEvent : public Event
+ {
+ public:
+ MouseMovedEvent(float x, float y)
+ : m_mouse_x(x), m_mouse_y(y) { }
+
+ float get_x() const { return m_mouse_x; }
+ float get_y() const { return m_mouse_y; }
+
+ std::string to_string() const override
+ {
+ return "MouseMovedEvent: " + std::to_string(m_mouse_x) + ", " + std::to_string(m_mouse_y);
+ }
+
+ MOUSE_EVENT_CLASS_TYPE(MouseMoved)
+
+ private:
+ float m_mouse_x, m_mouse_y;
+ };
+
+ class MouseScrolledEvent : public Event
+ {
+ public:
+ MouseScrolledEvent(float x_offset, float y_offset)
+ : m_x_offset(x_offset), m_y_offset(y_offset) { }
+
+ float get_x_offset() const { return m_x_offset; }
+ float get_y_offset() const { return m_y_offset; }
+
+ std::string to_string() const override
+ {
+ return "MouseScrolledEvent: " + std::to_string(m_x_offset) +
+ ", " + std::to_string(m_y_offset);
+ }
+
+ MOUSE_EVENT_CLASS_TYPE(MouseScrolled)
+
+ private:
+ float m_x_offset, m_y_offset;
+ };
+
+ class MouseButtonEvent : public Event
+ {
+ public:
+ int get_mouse_button() const { return m_button; }
+ virtual int get_category_flags() const override { return EventCategoryMouseButton |
+ EventCategoryMouse |
+ EventCategoryInput; }
+
+ protected:
+ MouseButtonEvent(int button) : m_button(button) { }
+ int m_button;
+ };
+
+ class MouseButtonPressedEvent : public MouseButtonEvent
+ {
+ public:
+ MouseButtonPressedEvent(int button) : MouseButtonEvent(button) { }
+
+ std::string to_string() const override
+ {
+ return "MouseButtonPressedEvent: " + std::to_string(m_button);
+ }
+
+ MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonPressed)
+ };
+
+ class MouseButtonReleasedEvent : public MouseButtonEvent
+ {
+ public:
+ MouseButtonReleasedEvent(int button) : MouseButtonEvent(button) { }
+
+ std::string to_string() const override
+ {
+ return "MouseButtonReleasedEvent: " + std::to_string(m_button);
+ }
+
+ MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonReleased)
+ };
+
+ class EventDispatcher
+ {
+ public:
+ EventDispatcher(Event& event) : m_event(event) { }
+
+ template<typename T, typename F>
+ bool dispatch(const F& func)
+ {
+ if (m_event.get_event_type() == T::get_static_type())
+ {
+ m_event.handled = func(static_cast<T&>(m_event));
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ Event& m_event;
+ };
+
+ class EventHandler
+ {
+ public:
+ using EventCallbackFn = std::function<void(Event&)>;
+
+ EventHandler() = default;
+ ~EventHandler() = default;
+
+ void set_event_callback(const EventCallbackFn& callback) { m_event_callback = callback; }
+
+ void on_event(Event& event)
+ {
+ if (m_event_callback)
+ m_event_callback(event);
+ }
+
+ template<typename T>
+ void bind_event(const std::function<void(T&)>& callback)
+ {
+ m_event_callback = [callback](Event& event)
+ {
+ if (event.get_event_type() == T::get_static_type())
+ callback(static_cast<T&>(event));
+ };
+ }
+
+ private:
+ EventCallbackFn m_event_callback;
+ };
+
+ #undef EVENT_CLASS_TYPE
+ #undef APPLICATION_EVENT_CLASS_TYPE
+ #undef MOUSE_EVENT_CLASS_TYPE
+ #undef MOUSE_BUTTON_EVENT_CLASS_TYPE
+ #undef KEYBOARD_EVENT_CLASS_TYPE
+}
diff --git a/src/core/window.cpp b/src/core/window.cpp
index 3a6ba2c..ab5b977 100644
--- a/src/core/window.cpp
+++ b/src/core/window.cpp
@@ -1,5 +1,6 @@
#include "window.h"
#include <cstdint>
+#include <iostream>
namespace Donut
{
@@ -7,21 +8,57 @@ namespace Donut
static uint32_t s_glfw_window_count = 0;
Window::Window(const std::string& title, int width, int height)
- : m_title(title), m_width(width), m_height(height)
+ : m_title(title), m_width(width),
+ m_height(height), m_is_closed(false)
+ {
+ init();
+ }
+
+ Window::~Window()
+ {
+ shutdown();
+ }
+
+ void Window::init()
{
if (!s_glfw_initialized)
{
- glfwInit();
+ int success = glfwInit();
+ if (!success)
+ {
+ std::cout << "Could not initialize GLFW!" << std::endl;
+ return;
+ }
+
s_glfw_initialized = true;
}
- m_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
+ m_window = glfwCreateWindow(m_width, m_height, m_title.c_str(), nullptr, nullptr);
+ if (!m_window)
+ {
+ std::cout << "Could not create GLFW window!" << std::endl;
+ glfwTerminate();
+ return;
+ }
+
glfwMakeContextCurrent(m_window);
+ glfwSetWindowUserPointer(m_window, this);
+
+ glfwSetErrorCallback(glfw_error_callback);
+ glfwSetWindowCloseCallback(m_window, glfw_window_close_callback);
+ glfwSetWindowSizeCallback(m_window, glfw_window_size_callback);
+ glfwSetWindowFocusCallback(m_window, glfw_window_focus_callback);
+ glfwSetWindowPosCallback(m_window, glfw_window_pos_callback);
+ glfwSetKeyCallback(m_window, glfw_key_callback);
+ glfwSetCharCallback(m_window, glfw_char_callback);
+ glfwSetMouseButtonCallback(m_window, glfw_mouse_button_callback);
+ glfwSetScrollCallback(m_window, glfw_mouse_scroll_callback);
+ glfwSetCursorPosCallback(m_window, glfw_cursor_pos_callback);
s_glfw_window_count++;
}
- Window::~Window()
+ void Window::shutdown()
{
glfwDestroyWindow(m_window);
s_glfw_window_count--;
@@ -41,6 +78,116 @@ namespace Donut
bool Window::should_close() const
{
- return glfwWindowShouldClose(m_window);
+ return glfwWindowShouldClose(m_window) || m_is_closed;
+ }
+
+ void Window::glfw_error_callback(int error, const char* description)
+ {
+ std::cout << "GLFW Error (" << error << "): " << description << std::endl;
+ }
+
+ void Window::glfw_window_close_callback(GLFWwindow* window)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ WindowCloseEvent event;
+ win->m_event_handler.on_event(event);
+ }
+
+ void Window::glfw_window_size_callback(GLFWwindow* window, int width, int height)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ win->m_width = width;
+ win->m_height = height;
+
+ WindowResizeEvent event(width, height);
+ win->m_event_handler.on_event(event);
+ }
+
+ void Window::glfw_window_focus_callback(GLFWwindow* window, int focused)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+
+ if (focused)
+ {
+ WindowFocusEvent event;
+ win->m_event_handler.on_event(event);
+ }
+ else
+ {
+ WindowLostFocusEvent event;
+ win->m_event_handler.on_event(event);
+ }
+ }
+
+ void Window::glfw_window_pos_callback(GLFWwindow* window, int xpos, int ypos)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ WindowMovedEvent event(xpos, ypos);
+ win->m_event_handler.on_event(event);
+ }
+
+ void Window::glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+
+ switch (action)
+ {
+ case GLFW_PRESS:
+ {
+ KeyPressedEvent event(key, false);
+ win->m_event_handler.on_event(event);
+ } break;
+ case GLFW_RELEASE:
+ {
+ KeyReleasedEvent event(key);
+ win->m_event_handler.on_event(event);
+ } break;
+ case GLFW_REPEAT:
+ {
+ KeyPressedEvent event(key, true);
+ win->m_event_handler.on_event(event);
+ } break;
+ }
+ }
+
+ void Window::glfw_char_callback(GLFWwindow* window, unsigned int keycode)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ KeyTypedEvent event(keycode);
+ win->m_event_handler.on_event(event);
+ }
+
+ void Window::glfw_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+
+ switch (action)
+ {
+ case GLFW_PRESS:
+ {
+ MouseButtonPressedEvent event(button);
+ win->m_event_handler.on_event(event);
+ } break;
+ case GLFW_RELEASE:
+ {
+ MouseButtonReleasedEvent event(button);
+ win->m_event_handler.on_event(event);
+ break;
+ }
+ }
+ }
+
+ void Window::glfw_mouse_scroll_callback(GLFWwindow* window, double x_offset, double y_offset)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ MouseScrolledEvent event((float)x_offset, (float)y_offset);
+ win->m_event_handler.on_event(event);
+ }
+
+ void Window::glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos)
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ MouseMovedEvent event((float)x_pos, (float)y_pos);
+ win->m_event_handler.on_event(event);
}
}
diff --git a/src/core/window.h b/src/core/window.h
index 5f0e36e..8e9778e 100644
--- a/src/core/window.h
+++ b/src/core/window.h
@@ -2,23 +2,51 @@
#include <GLFW/glfw3.h>
#include <string>
+#include "event.h"
namespace Donut
{
class Window
{
public:
+ using EventCallbackFn = std::function<void(Event&)>;
+
Window(const std::string& title, int width, int height);
~Window();
bool should_close() const;
void on_update() const;
+
+ void set_event_callback(const EventCallbackFn& callback) { m_event_handler.set_event_callback(callback); }
+ EventHandler& get_event_handler() { return m_event_handler; }
+
+ unsigned int get_width() const { return m_width; }
+ unsigned int get_height() const { return m_height; }
+
+ void* get_native_window() const { return m_window; }
+
+ private:
+ void init();
+ void shutdown();
+
+ static void glfw_error_callback(int error, const char* description);
+ static void glfw_window_close_callback(GLFWwindow* window);
+ static void glfw_window_size_callback(GLFWwindow* window, int width, int height);
+ static void glfw_window_focus_callback(GLFWwindow* window, int focused);
+ static void glfw_window_pos_callback(GLFWwindow* window, int xpos, int ypos);
+ static void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
+ static void glfw_char_callback(GLFWwindow* window, unsigned int keycode);
+ static void glfw_mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
+ static void glfw_mouse_scroll_callback(GLFWwindow* window, double x_offset, double y_offset);
+ static void glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos);
+
private:
GLFWwindow* m_window;
+ EventHandler m_event_handler;
std::string m_title;
- int m_width;
- int m_height;
+ unsigned int m_width;
+ unsigned int m_height;
bool m_is_closed;
};
}