new event and window abstraction, preparing for multiple windows

This commit is contained in:
2026-04-05 17:41:58 +02:00
parent 3925ccc520
commit 9974471b66
9 changed files with 444 additions and 74 deletions

View File

@@ -2,19 +2,24 @@
#include "Event.h" #include "Event.h"
#include "Layer.h" #include "Layer.h"
#include "Log.h" #include "Log.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_main.h" #include "SDL3/SDL_main.h"
#include "SDL3/SDL_render.h" #include "SDL3/SDL_render.h"
#include "SDL3/SDL_stdinc.h" #include "SDL3/SDL_stdinc.h"
#include "SDL3/SDL_timer.h" #include "SDL3/SDL_timer.h"
#include "SDL3/SDL_video.h"
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_sdl3.h" #include "imgui_impl_sdl3.h"
#include "imguiinit.h" #include "imguiinit.h"
#include <memory>
#include <ranges>
#define SDL_MAIN_HANDLED 1 #define SDL_MAIN_HANDLED 1
namespace SakuraVNE {
Application *Application::s_Instance = nullptr; 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(); SDL_SetMainReady();
m_initResult = Init(); m_initResult = Init();
} }
@@ -37,34 +42,21 @@ bool Application::Init() {
return false; 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:"); LOG_INFO("Available renderer drivers:");
for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) { for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) {
LOG_INFO("{0}. {1}", i + 1, SDL_GetRenderDriver(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<Window>(m_AppData.windowdata));
m_Window[0]->Create();
m_Renderer = SDL_CreateRenderer(GetSDLWindow(), nullptr);
if (!m_Renderer) { if (!m_Renderer) {
LOG_ERROR("Renderer could not be created! {0}", SDL_GetError()); LOG_ERROR("Renderer could not be created! {0}", SDL_GetError());
} else { } else {
@@ -72,7 +64,7 @@ bool Application::Init() {
LOG_INFO("Renderer: {0}", SDL_GetRendererName(m_Renderer)); 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(); m_ImGui = new SakuraVNE::ImGuiInit();
@@ -100,7 +92,6 @@ void Application::Run() {
} }
// Events // Events
// TODO: can i connect my events to sdl events and use their dispatcher?
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
@@ -111,14 +102,35 @@ void Application::Run() {
LOG_INFO("Running state: {0}", GetRunningState()); LOG_INFO("Running state: {0}", GetRunningState());
} }
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(GetSDLWindow())) { SDL_WindowID targetWindowID = 0;
SetRunningState(false);
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) { if (targetWindowID != 0) {
SDL_GetWindowSize(GetSDLWindow(), &GetAppData().windowdata.width, &GetAppData().windowdata.height); 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;
}
} }
} }
@@ -138,13 +150,26 @@ void Application::Run() {
SDL_RenderPresent(m_Renderer); SDL_RenderPresent(m_Renderer);
} }
} }
}
void Application::Shutdown() { void Application::Shutdown() {
LOG_WARN("Shutting down the application!"); LOG_WARN("Shutting down the application!");
SDL_DestroyRenderer(m_Renderer); SDL_DestroyRenderer(m_Renderer);
SDL_DestroyWindow(m_Window); m_Window.clear();
SDL_Quit(); 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

View File

@@ -1,20 +1,17 @@
#pragma once #pragma once
#include "Event.h"
#include "Layer.h" #include "Layer.h"
#include "LayerStack.h" #include "LayerStack.h"
#include "SDL3/SDL_render.h" #include "SDL3/SDL_render.h"
#include "SDL3/SDL_video.h" #include "SDL3/SDL_video.h"
#include "Window.h"
#include "imguiinit.h" #include "imguiinit.h"
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <vector>
struct WindowData { namespace SakuraVNE {
int width = 1280;
int height = 720;
int pos_x = -1;
int pos_y = -1;
std::string title = "Window Title";
};
struct AppData { struct AppData {
std::string name = "Application"; std::string name = "Application";
@@ -29,12 +26,13 @@ public:
bool Init(); bool Init();
void Run(); void Run();
void Shutdown(); void Shutdown();
void RaiseEvent(Event &e);
template <typename TLayer> template <typename TLayer>
requires(std::is_base_of_v<SakuraVNE::Layer, TLayer>) requires(std::is_base_of_v<Layer, TLayer>)
void PushLayer() { void PushLayer() {
auto newLayer = std::make_unique<TLayer>(); auto newLayer = std::make_unique<TLayer>();
SakuraVNE::Layer *layer = newLayer.get(); Layer *layer = newLayer.get();
m_LayerStack.PushLayer(std::move(newLayer)); m_LayerStack.PushLayer(std::move(newLayer));
@@ -42,7 +40,7 @@ public:
} }
template <typename TLayer> template <typename TLayer>
requires(std::is_base_of_v<SakuraVNE::Layer, TLayer>) requires(std::is_base_of_v<Layer, TLayer>)
TLayer *GetLayer() { TLayer *GetLayer() {
for (const auto &layer : m_LayerStack) { for (const auto &layer : m_LayerStack) {
if (auto casted = dynamic_cast<TLayer *>(layer.get())) { if (auto casted = dynamic_cast<TLayer *>(layer.get())) {
@@ -53,7 +51,7 @@ public:
} }
template <typename TLayer> template <typename TLayer>
requires(std::is_base_of_v<SakuraVNE::Layer, TLayer>) requires(std::is_base_of_v<Layer, TLayer>)
TLayer *GetLayer(const std::string &layerName) { TLayer *GetLayer(const std::string &layerName) {
for (const auto &layer : m_LayerStack) { for (const auto &layer : m_LayerStack) {
if (auto casted = dynamic_cast<TLayer *>(layer.get()) && layer->GetName() == layerName) { if (auto casted = dynamic_cast<TLayer *>(layer.get()) && layer->GetName() == layerName) {
@@ -64,10 +62,10 @@ public:
} }
template <typename TLayer> template <typename TLayer>
requires(std::is_base_of_v<SakuraVNE::Layer, TLayer>) requires(std::is_base_of_v<Layer, TLayer>)
void PushOverlay() { void PushOverlay() {
auto newOverlay = std::make_unique<TLayer>(); auto newOverlay = std::make_unique<TLayer>();
SakuraVNE::Layer *layer = newOverlay.get(); Layer *layer = newOverlay.get();
m_LayerStack.PushOverLay(std::move(newOverlay)); m_LayerStack.PushOverLay(std::move(newOverlay));
@@ -75,11 +73,13 @@ public:
} }
inline AppData &GetAppData() { return m_AppData; } 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_Renderer *GetSDLRenderer() { return m_Renderer; }
inline SDL_Surface *GetSDLWindowSurface() { return m_Surface; } inline SDL_Surface *GetSDLWindowSurface() { return m_Surface; }
inline void SetSDLWindowSurface(SDL_Surface *newSurface) { m_Surface = newSurface; } inline void SetSDLWindowSurface(SDL_Surface *newSurface) { m_Surface = newSurface; }
std::shared_ptr<Window> GetWindow(const int index = 0) const { return m_Window[index]; }
bool &GetRunningState() { return m_isRunning; } bool &GetRunningState() { return m_isRunning; }
void SetRunningState(bool isRunning) { m_isRunning = isRunning; } void SetRunningState(bool isRunning) { m_isRunning = isRunning; }
@@ -89,16 +89,17 @@ private:
bool m_isRunning; bool m_isRunning;
bool m_initResult; bool m_initResult;
SDL_Window *m_Window;
SDL_Surface *m_Surface; SDL_Surface *m_Surface;
SDL_Renderer *m_Renderer; SDL_Renderer *m_Renderer;
AppData m_AppData; AppData m_AppData;
std::vector<std::shared_ptr<Window>> m_Window;
SakuraVNE::LayerStack m_LayerStack; LayerStack m_LayerStack;
SakuraVNE::ImGuiInit *m_ImGui; ImGuiInit *m_ImGui;
static Application *s_Instance; static Application *s_Instance;
friend class SakuraVNE::Layer; friend class Layer;
}; };
} // namespace SakuraVNE

View File

@@ -1,6 +1 @@
#include "Event.h" #include "Event.h"
#include "Application.h"
#include "Log.h"
#include "SDL3/SDL.h"
void SakuraVNE::ProcessEvents() {}

View File

@@ -1,3 +1,51 @@
#pragma once
#include <functional>
#include <string>
namespace SakuraVNE { namespace SakuraVNE {
void ProcessEvents(); 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 <typename T> using EventFn = std::function<bool(T &)>;
public:
EventDispatcher(Event &event) : m_Event(event) {}
template <typename T> bool Dispatch(EventFn<T> 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

View File

@@ -0,0 +1,94 @@
#pragma once
#include "Event.h"
#include <format>
#include <string>
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

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "Event.h"
#include "SDL3/SDL_stdinc.h" #include "SDL3/SDL_stdinc.h"
#include <concepts> #include <concepts>
#include <memory> #include <memory>
@@ -12,7 +13,7 @@ public:
virtual ~Layer() = default; virtual ~Layer() = default;
virtual void OnFrame(Uint64 timestamp) {} virtual void OnFrame(Uint64 timestamp) {}
virtual void OnEvent(/*Event &e*/) {} virtual void OnEvent(Event &e) {}
virtual void OnAttach() {} virtual void OnAttach() {}
virtual void OnDetach() {} virtual void OnDetach() {}
virtual void OnImGuiRender() {} virtual void OnImGuiRender() {}

127
SakuraCore/src/Window.cpp Normal file
View File

@@ -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

50
SakuraCore/src/Window.h Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
#include "Event.h"
#include "SDL3/SDL_video.h"
#include "imgui_impl_sdl3.h"
#include <cstdint>
#include <functional>
#include <string>
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<void(Event &)>;
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

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Event.h"
#include <cstdint>
#include <format>
#include <string>
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