From 9974471b66113a3caf20ad40f3a1db44654bca6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hatvani=20Tam=C3=A1s?= Date: Sun, 5 Apr 2026 17:41:58 +0200 Subject: [PATCH] new event and window abstraction, preparing for multiple windows --- SakuraCore/src/Application.cpp | 119 ++++++++++++++++++------------ SakuraCore/src/Application.h | 37 +++++----- SakuraCore/src/Event.cpp | 5 -- SakuraCore/src/Event.h | 54 +++++++++++++- SakuraCore/src/InputEvents.h | 94 ++++++++++++++++++++++++ SakuraCore/src/Layer.h | 3 +- SakuraCore/src/Window.cpp | 127 +++++++++++++++++++++++++++++++++ SakuraCore/src/Window.h | 50 +++++++++++++ SakuraCore/src/WindowEvents.h | 29 ++++++++ 9 files changed, 444 insertions(+), 74 deletions(-) create mode 100644 SakuraCore/src/InputEvents.h create mode 100644 SakuraCore/src/Window.cpp create mode 100644 SakuraCore/src/Window.h create mode 100644 SakuraCore/src/WindowEvents.h diff --git a/SakuraCore/src/Application.cpp b/SakuraCore/src/Application.cpp index ce6dc25..d159cc7 100644 --- a/SakuraCore/src/Application.cpp +++ b/SakuraCore/src/Application.cpp @@ -2,19 +2,24 @@ #include "Event.h" #include "Layer.h" #include "Log.h" +#include "SDL3/SDL_events.h" #include "SDL3/SDL_main.h" #include "SDL3/SDL_render.h" #include "SDL3/SDL_stdinc.h" #include "SDL3/SDL_timer.h" +#include "SDL3/SDL_video.h" #include "imgui.h" #include "imgui_impl_sdl3.h" #include "imguiinit.h" +#include +#include #define SDL_MAIN_HANDLED 1 +namespace SakuraVNE { Application *Application::s_Instance = nullptr; -Application::Application(const AppData &appdata) : m_Window(nullptr), m_Renderer(nullptr), m_Surface(nullptr), m_isRunning(false), m_AppData(appdata) { +Application::Application(const AppData &appdata) : m_Window(), m_Renderer(nullptr), m_Surface(nullptr), m_isRunning(false), m_AppData(appdata) { SDL_SetMainReady(); m_initResult = Init(); } @@ -37,34 +42,21 @@ bool Application::Init() { return false; } - SDL_WindowFlags windowFlags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); - m_Window = SDL_CreateWindow(GetAppData().windowdata.title.c_str(), GetAppData().windowdata.width, GetAppData().windowdata.height, windowFlags); - - if (!m_Window) { - LOG_ERROR("SDL window could not be created! {0}", SDL_GetError()); - Shutdown(); - - return false; - } else { - LOG_INFO("SDl window created"); - } - - if (GetAppData().windowdata.pos_x != -1 && GetAppData().windowdata.pos_y != -1) { - if (!SDL_SetWindowPosition(m_Window, GetAppData().windowdata.pos_x, GetAppData().windowdata.pos_y)) { - LOG_ERROR("Failed to set SDL_Window position {0}", SDL_GetError()); - } else { - LOG_INFO("SDL window position set to the initial value: x {0}, y {1}", GetAppData().windowdata.pos_x, GetAppData().windowdata.pos_y); - } - } else { - LOG_WARN("SDL window position not set. Will not attempt to set window position."); - } - LOG_INFO("Available renderer drivers:"); for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) { LOG_INFO("{0}. {1}", i + 1, SDL_GetRenderDriver(i)); } - m_Renderer = SDL_CreateRenderer(m_Window, nullptr); + // TODO: check here if m_Window is not null otherwise shutdown procedure + if (m_AppData.windowdata.title.empty()) { + m_AppData.windowdata.title = m_AppData.name; + } + + m_AppData.windowdata.eventCallback = [this](Event &event) { RaiseEvent(event); }; + m_Window.push_back(std::make_shared(m_AppData.windowdata)); + m_Window[0]->Create(); + + m_Renderer = SDL_CreateRenderer(GetSDLWindow(), nullptr); if (!m_Renderer) { LOG_ERROR("Renderer could not be created! {0}", SDL_GetError()); } else { @@ -72,7 +64,7 @@ bool Application::Init() { LOG_INFO("Renderer: {0}", SDL_GetRendererName(m_Renderer)); } - SDL_SetRenderVSync(m_Renderer, 1); + SDL_SetRenderVSync(m_Renderer, m_AppData.windowdata.isVsync); m_ImGui = new SakuraVNE::ImGuiInit(); @@ -100,7 +92,6 @@ void Application::Run() { } // Events - // TODO: can i connect my events to sdl events and use their dispatcher? SDL_Event event; while (SDL_PollEvent(&event)) { @@ -111,31 +102,53 @@ void Application::Run() { LOG_INFO("Running state: {0}", GetRunningState()); } - if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(GetSDLWindow())) { - SetRunningState(false); + SDL_WindowID targetWindowID = 0; + + if (event.type >= SDL_EVENT_WINDOW_FIRST && event.type <= SDL_EVENT_WINDOW_LAST) { + targetWindowID = event.window.windowID; + } else if (event.type >= SDL_EVENT_KEY_DOWN && event.type <= SDL_EVENT_KEY_UP) { + targetWindowID = event.key.windowID; + } else if (event.type >= SDL_EVENT_MOUSE_MOTION && event.type <= SDL_EVENT_MOUSE_WHEEL) { + targetWindowID = event.motion.windowID; } - if (event.type == SDL_EVENT_WINDOW_RESIZED) { - SDL_GetWindowSize(GetSDLWindow(), &GetAppData().windowdata.width, &GetAppData().windowdata.height); + if (targetWindowID != 0) { + for (auto it = m_Window.begin(); it != m_Window.end();) { - SetSDLWindowSurface(SDL_GetWindowSurface(GetSDLWindow())); + if (SDL_GetWindowID((*it)->GetHandle()) == targetWindowID) { + (*it)->ProcessEvent(event); + + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { + it = m_Window.erase(it); + + if (m_Window.empty()) { + SetRunningState(false); + } + } else { + ++it; + } + break; + } else { + ++it; + } + } } + + // Rendering + m_ImGui->Begin(); + + SDL_SetRenderScale(m_Renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); + SDL_SetRenderDrawColor(m_Renderer, (Uint8)111, (Uint8)232, (Uint8)168, (Uint8)0); + SDL_RenderClear(m_Renderer); + + for (auto &layer : m_LayerStack) { + layer->OnImGuiRender(); + } + + m_ImGui->End(); + + SDL_RenderPresent(m_Renderer); } - - // Rendering - m_ImGui->Begin(); - - SDL_SetRenderScale(m_Renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); - SDL_SetRenderDrawColor(m_Renderer, (Uint8)111, (Uint8)232, (Uint8)168, (Uint8)0); - SDL_RenderClear(m_Renderer); - - for (auto &layer : m_LayerStack) { - layer->OnImGuiRender(); - } - - m_ImGui->End(); - - SDL_RenderPresent(m_Renderer); } } @@ -144,7 +157,19 @@ void Application::Shutdown() { SDL_DestroyRenderer(m_Renderer); - SDL_DestroyWindow(m_Window); + m_Window.clear(); SDL_Quit(); + + s_Instance = nullptr; } + +void Application::RaiseEvent(Event &event) { + for (auto &layer : std::views::reverse(m_LayerStack)) { + layer->OnEvent(event); + if (event.Handled) { + break; + } + } +} +} // namespace SakuraVNE diff --git a/SakuraCore/src/Application.h b/SakuraCore/src/Application.h index a7a6ca5..b4cc386 100644 --- a/SakuraCore/src/Application.h +++ b/SakuraCore/src/Application.h @@ -1,20 +1,17 @@ #pragma once +#include "Event.h" #include "Layer.h" #include "LayerStack.h" #include "SDL3/SDL_render.h" #include "SDL3/SDL_video.h" +#include "Window.h" #include "imguiinit.h" #include #include +#include -struct WindowData { - int width = 1280; - int height = 720; - int pos_x = -1; - int pos_y = -1; - std::string title = "Window Title"; -}; +namespace SakuraVNE { struct AppData { std::string name = "Application"; @@ -29,12 +26,13 @@ public: bool Init(); void Run(); void Shutdown(); + void RaiseEvent(Event &e); template - requires(std::is_base_of_v) + requires(std::is_base_of_v) void PushLayer() { auto newLayer = std::make_unique(); - SakuraVNE::Layer *layer = newLayer.get(); + Layer *layer = newLayer.get(); m_LayerStack.PushLayer(std::move(newLayer)); @@ -42,7 +40,7 @@ public: } template - requires(std::is_base_of_v) + requires(std::is_base_of_v) TLayer *GetLayer() { for (const auto &layer : m_LayerStack) { if (auto casted = dynamic_cast(layer.get())) { @@ -53,7 +51,7 @@ public: } template - requires(std::is_base_of_v) + requires(std::is_base_of_v) TLayer *GetLayer(const std::string &layerName) { for (const auto &layer : m_LayerStack) { if (auto casted = dynamic_cast(layer.get()) && layer->GetName() == layerName) { @@ -64,10 +62,10 @@ public: } template - requires(std::is_base_of_v) + requires(std::is_base_of_v) void PushOverlay() { auto newOverlay = std::make_unique(); - SakuraVNE::Layer *layer = newOverlay.get(); + Layer *layer = newOverlay.get(); m_LayerStack.PushOverLay(std::move(newOverlay)); @@ -75,11 +73,13 @@ public: } inline AppData &GetAppData() { return m_AppData; } - inline SDL_Window *GetSDLWindow() { return m_Window; } + inline SDL_Window *GetSDLWindow(const int index = 0) { return m_Window[index]->GetHandle(); } inline SDL_Renderer *GetSDLRenderer() { return m_Renderer; } inline SDL_Surface *GetSDLWindowSurface() { return m_Surface; } inline void SetSDLWindowSurface(SDL_Surface *newSurface) { m_Surface = newSurface; } + std::shared_ptr GetWindow(const int index = 0) const { return m_Window[index]; } + bool &GetRunningState() { return m_isRunning; } void SetRunningState(bool isRunning) { m_isRunning = isRunning; } @@ -89,16 +89,17 @@ private: bool m_isRunning; bool m_initResult; - SDL_Window *m_Window; SDL_Surface *m_Surface; SDL_Renderer *m_Renderer; AppData m_AppData; + std::vector> m_Window; - SakuraVNE::LayerStack m_LayerStack; - SakuraVNE::ImGuiInit *m_ImGui; + LayerStack m_LayerStack; + ImGuiInit *m_ImGui; static Application *s_Instance; - friend class SakuraVNE::Layer; + friend class Layer; }; +} // namespace SakuraVNE diff --git a/SakuraCore/src/Event.cpp b/SakuraCore/src/Event.cpp index b4a4691..642094d 100644 --- a/SakuraCore/src/Event.cpp +++ b/SakuraCore/src/Event.cpp @@ -1,6 +1 @@ #include "Event.h" -#include "Application.h" -#include "Log.h" -#include "SDL3/SDL.h" - -void SakuraVNE::ProcessEvents() {} diff --git a/SakuraCore/src/Event.h b/SakuraCore/src/Event.h index 144073c..c0d7e58 100644 --- a/SakuraCore/src/Event.h +++ b/SakuraCore/src/Event.h @@ -1,3 +1,51 @@ -namespace SakuraVNE{ - void ProcessEvents(); -} +#pragma once + +#include +#include + +namespace SakuraVNE { +enum class EventType { + None = 0, + WindowClose, + WindowResize, + KeyPressed, + KeyReleased, + MouseButtonPressed, + MouseButtonReleased, + MouseMoved, + MouseScrolled, +}; + +#define EVENT_CLASS_TYPE(type) \ + static EventType GetStaticType() { return EventType::type; } \ + virtual EventType GetEventType() const override { return GetStaticType(); } \ + virtual const char *GetName() const override { return #type; } + +class Event { +public: + bool Handled = false; + + virtual ~Event() {} + virtual EventType GetEventType() const = 0; + virtual const char *GetName() const = 0; + virtual std::string ToString() const { return GetName(); } +}; + +class EventDispatcher { + template using EventFn = std::function; + +public: + EventDispatcher(Event &event) : m_Event(event) {} + + template bool Dispatch(EventFn func) { + if (m_Event.GetEventType() == T::GetStaticType() && !m_Event.Handled) { + m_Event.Handled = func(*(T *)&m_Event); + return true; + } + return false; + } + +private: + Event &m_Event; +}; +} // namespace SakuraVNE diff --git a/SakuraCore/src/InputEvents.h b/SakuraCore/src/InputEvents.h new file mode 100644 index 0000000..4cc9e45 --- /dev/null +++ b/SakuraCore/src/InputEvents.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Event.h" + +#include +#include + +namespace SakuraVNE { +// Key events +class KeyEvent : public Event { +public: + inline int GetKeyCode() const { return m_KeyCode; } + +protected: + KeyEvent(int keycode) : m_KeyCode(keycode) {} + int m_KeyCode; +}; + +class KeyPressedEvent : public KeyEvent { +public: + KeyPressedEvent(int keycode, bool isRepeat) : KeyEvent(keycode), m_IsRepeat(isRepeat) {} + inline bool IsRepeat() const { return m_IsRepeat; } + std::string ToString() const override { return std::format("KeyPressedEvent: {} (repeat={})", m_KeyCode, m_IsRepeat); } + EVENT_CLASS_TYPE(KeyPressed) + +private: + bool m_IsRepeat; +}; + +class KeyReleasedEvent : public KeyEvent { +public: + KeyReleasedEvent(int keycode) : KeyEvent(keycode) {} + std::string ToString() const override { return std::format("KeyReleasedEvent: {}", m_KeyCode); } + + EVENT_CLASS_TYPE(KeyReleased) +}; + +// Mouse events +class MouseMovedEvent : public Event { +public: + MouseMovedEvent(double x, double y) : m_MouseX(x), m_MouseY(y) {} + + inline double GetX() const { return m_MouseX; } + inline double GetY() const { return m_MouseY; } + + std::string ToString() { return std::format("MouseMovedEvent: {}, {}", m_MouseX, m_MouseY); } + + EVENT_CLASS_TYPE(MouseMoved) +private: + double m_MouseX, m_MouseY; +}; + +class MouseScrolledEvent : public Event { +public: + MouseScrolledEvent(double xOffset, double yOffset) : m_XOffset(xOffset), m_YOffset(yOffset) {} + + inline double GetXOffset() const { return m_XOffset; } + inline double GetYOffset() const { return m_YOffset; } + + std::string ToString() const override { return std::format("MouseScrolledEvent: {}, {}", m_XOffset, m_YOffset); } + + EVENT_CLASS_TYPE(MouseScrolled) +private: + double m_XOffset, m_YOffset; +}; + +class MouseButtonEvent : public Event { +public: + inline int GetMouseButton() const { return m_Button; } + +protected: + MouseButtonEvent(int button) : m_Button(button) {} + + int m_Button; +}; + +class MouseButtonPressedEvent : public MouseButtonEvent { +public: + MouseButtonPressedEvent(int button) : MouseButtonEvent(button) {} + + std::string ToString() const override { return std::format("MouseButtonPressedEvent: {}", m_Button); } + + EVENT_CLASS_TYPE(MouseButtonPressed) +}; + +class MouseButtonReleasedEvent : public MouseButtonEvent { +public: + MouseButtonReleasedEvent(int button) : MouseButtonEvent(button) {} + + std::string ToString() const override { return std::format("MouseButtonReleasedEvent: {}", m_Button); } + + EVENT_CLASS_TYPE(MouseButtonReleased) +}; +} // namespace SakuraVNE diff --git a/SakuraCore/src/Layer.h b/SakuraCore/src/Layer.h index c977b59..695a0e4 100644 --- a/SakuraCore/src/Layer.h +++ b/SakuraCore/src/Layer.h @@ -1,5 +1,6 @@ #pragma once +#include "Event.h" #include "SDL3/SDL_stdinc.h" #include #include @@ -12,7 +13,7 @@ public: virtual ~Layer() = default; virtual void OnFrame(Uint64 timestamp) {} - virtual void OnEvent(/*Event &e*/) {} + virtual void OnEvent(Event &e) {} virtual void OnAttach() {} virtual void OnDetach() {} virtual void OnImGuiRender() {} diff --git a/SakuraCore/src/Window.cpp b/SakuraCore/src/Window.cpp new file mode 100644 index 0000000..c8f64d2 --- /dev/null +++ b/SakuraCore/src/Window.cpp @@ -0,0 +1,127 @@ +#include "Window.h" + +#include "Application.h" +#include "InputEvents.h" +#include "Log.h" +#include "SDL3/SDL_events.h" +#include "SDL3/SDL_mouse.h" +#include "SDL3/SDL_video.h" +#include "WindowEvents.h" + +namespace SakuraVNE { +Window::Window(const WindowData &data) : m_Data(data) {} +Window::~Window() { Destroy(); } + +void Window::Create() { + // TODO: maybe get an unknow amount of parameters / an array of window flags? or just provide a seperate function for it + SDL_WindowFlags windowFlags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); + m_Handle = SDL_CreateWindow(m_Data.title.c_str(), m_Data.width, m_Data.height, windowFlags); + + if (!m_Handle) { + LOG_ERROR("SDL window could not be created! {0}", SDL_GetError()); + // FIX: there has to be a better way to shutdown this cannot be correct, because i just quit the sdl stuff but the init continues + Application::Get().Shutdown(); + return; + } else { + LOG_INFO("SDl window created"); + } + + if (m_Data.pos_x != -1 && m_Data.pos_y != -1) { + if (!SDL_SetWindowPosition(m_Handle, m_Data.pos_x, m_Data.pos_y)) { + LOG_ERROR("Failed to set SDL_Window position {0}", SDL_GetError()); + } else { + LOG_INFO("SDL window position set to the initial value: x {0}, y {1}", m_Data.pos_x, m_Data.pos_y); + } + } else { + LOG_WARN("SDL window position not set. Will not attempt to set window position."); + } +} + +// TODO: add the window callbacks here as seperate functions because sdl functions that way +// each of them will get a function which will receive a window event filled in with the window's data (id (i guess)) +// and then they will dispatch it to the event system using the same raise event + +void Window::ProcessEvent(const SDL_Event &event) { + switch (event.type) { + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { + WindowClosedEvent e; + RaiseEvent(e); + break; + } + + case SDL_EVENT_WINDOW_RESIZED: { + WindowResizeEvent e((uint32_t)event.window.data1, (uint32_t)event.window.data2); + RaiseEvent(e); + break; + } + + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: { + bool isRepeat = event.key.repeat != 0; + if (event.type == SDL_EVENT_KEY_DOWN) { + KeyPressedEvent e(event.key.key, isRepeat); + RaiseEvent(e); + } else { + KeyReleasedEvent e(event.key.key); + RaiseEvent(e); + } + break; + } + + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: { + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { + MouseButtonPressedEvent e(event.button.button); + RaiseEvent(e); + } else { + MouseButtonReleasedEvent e(event.button.button); + RaiseEvent(e); + } + break; + } + + case SDL_EVENT_MOUSE_MOTION: { + MouseMovedEvent e(event.motion.x, event.motion.y); + RaiseEvent(e); + break; + } + + case SDL_EVENT_MOUSE_WHEEL: { + MouseScrolledEvent e(event.wheel.x, event.wheel.y); + RaiseEvent(e); + break; + } + } +} + +void Window::Resize() { + SDL_GetWindowSize(m_Handle, &m_Data.width, &m_Data.height); + SDL_SetWindowSize(m_Handle, m_Data.width, m_Data.height); +} + +void Window::Destroy() { + if (m_Handle) { + SDL_DestroyWindow(m_Handle); + } + m_Handle = nullptr; +} + +void Window::RaiseEvent(Event &event) { + if (m_Data.eventCallback) { + m_Data.eventCallback(event); + } +} + +// TODO: this need testing because i am really not sure this is correct +// should this be static or go somewhere else | probably in application? +auto Window::GetMousePos() const { + struct result { + float *x; + float *y; + }; + + SDL_GetMouseState(m_MouseXPos, m_MouseYPos); + + return result{m_MouseXPos, m_MouseYPos}; +} +} // namespace SakuraVNE diff --git a/SakuraCore/src/Window.h b/SakuraCore/src/Window.h new file mode 100644 index 0000000..a7a8879 --- /dev/null +++ b/SakuraCore/src/Window.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Event.h" +#include "SDL3/SDL_video.h" +#include "imgui_impl_sdl3.h" +#include +#include +#include + +namespace SakuraVNE { +struct WindowData { + std::string title = "Window Title"; + int width = 1280; + int height = 720; + int pos_x = -1; + int pos_y = -1; + bool isResizable = true; + bool isVsync = true; + + using EventCallbackFn = std::function; + EventCallbackFn eventCallback; +}; + +class Window { +public: + Window(const WindowData &data = WindowData()); + ~Window(); + + void Create(); + void Destroy(); + + void Update(); + void Resize(); + void ProcessEvent(const SDL_Event &event); + + void RaiseEvent(Event &event); + + bool ShouldClose() const; + auto GetMousePos() const; + + SDL_Window *GetHandle() const { return m_Handle; } + +private: + WindowData m_Data; + SDL_Window *m_Handle = nullptr; + float *m_MouseXPos = nullptr; + float *m_MouseYPos = nullptr; +}; + +} // namespace SakuraVNE diff --git a/SakuraCore/src/WindowEvents.h b/SakuraCore/src/WindowEvents.h new file mode 100644 index 0000000..2a5f725 --- /dev/null +++ b/SakuraCore/src/WindowEvents.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Event.h" + +#include +#include +#include + +namespace SakuraVNE { +class WindowClosedEvent : public Event { +public: + WindowClosedEvent() {} + EVENT_CLASS_TYPE(WindowClose) +}; + +class WindowResizeEvent : public Event { +public: + WindowResizeEvent(uint32_t width, uint32_t height) : m_Width(width), m_Height(height) {} + + // TODO: chech if uint32_t works with sdl3 windows + inline uint32_t GetWidth() const { return m_Width; } + inline uint32_t GetHeight() const { return m_Height; } + + std::string ToString() const override { return std::format("WindowResizeEvent: {}, {}", m_Width, m_Height); } + EVENT_CLASS_TYPE(WindowResize) +private: + uint32_t m_Width, m_Height; +}; +} // namespace SakuraVNE