mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Implement event bus and service registry for decoupled communication and lifecycle management
This commit is contained in:
@@ -121,6 +121,8 @@ if(BUILD_SDL3_APP)
|
||||
src/logging/logger.cpp
|
||||
src/logging/string_utils.cpp
|
||||
src/core/platform.cpp
|
||||
src/di/service_registry.cpp
|
||||
src/events/event_bus.cpp
|
||||
src/app/sdl3_app_core.cpp
|
||||
src/app/audio_player.cpp
|
||||
src/app/sdl3_app_device.cpp
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"conan": {}
|
||||
},
|
||||
"include": [
|
||||
"build/Release/generators/CMakePresets.json"
|
||||
"build/Release/generators/CMakePresets.json",
|
||||
"build/build/Release/generators/CMakePresets.json"
|
||||
]
|
||||
}
|
||||
56
src/di/lifecycle.hpp
Normal file
56
src/di/lifecycle.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
namespace sdl3cpp::di {
|
||||
|
||||
/**
|
||||
* @brief Interface for services that require initialization.
|
||||
*
|
||||
* Similar to Spring's @PostConstruct, this interface allows services
|
||||
* to perform initialization logic after construction and dependency injection.
|
||||
*
|
||||
* The ServiceRegistry will call Initialize() on all registered services
|
||||
* that implement this interface when InitializeAll() is invoked.
|
||||
*/
|
||||
class IInitializable {
|
||||
public:
|
||||
virtual ~IInitializable() = default;
|
||||
|
||||
/**
|
||||
* @brief Initialize the service.
|
||||
*
|
||||
* Called once after the service is constructed and all dependencies
|
||||
* are injected. This is where you should perform initialization logic
|
||||
* such as loading resources, connecting to external services, etc.
|
||||
*
|
||||
* @throws std::runtime_error if initialization fails
|
||||
*/
|
||||
virtual void Initialize() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Interface for services that require cleanup on shutdown.
|
||||
*
|
||||
* Similar to Spring's @PreDestroy, this interface allows services
|
||||
* to perform cleanup logic before destruction.
|
||||
*
|
||||
* The ServiceRegistry will call Shutdown() on all registered services
|
||||
* that implement this interface when ShutdownAll() is invoked,
|
||||
* in reverse order of registration.
|
||||
*/
|
||||
class IShutdownable {
|
||||
public:
|
||||
virtual ~IShutdownable() = default;
|
||||
|
||||
/**
|
||||
* @brief Shutdown the service and release resources.
|
||||
*
|
||||
* Called once before the service is destroyed. This is where you
|
||||
* should perform cleanup logic such as closing connections, releasing
|
||||
* resources, saving state, etc.
|
||||
*
|
||||
* This method should not throw exceptions.
|
||||
*/
|
||||
virtual void Shutdown() noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::di
|
||||
37
src/di/service_registry.cpp
Normal file
37
src/di/service_registry.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "service_registry.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace sdl3cpp::di {
|
||||
|
||||
void ServiceRegistry::InitializeAll() {
|
||||
if (initialized_) {
|
||||
throw std::runtime_error("Services already initialized");
|
||||
}
|
||||
|
||||
// Call all initialization functions in registration order
|
||||
for (const auto& initFunc : initFunctions_) {
|
||||
initFunc();
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void ServiceRegistry::ShutdownAll() noexcept {
|
||||
if (!initialized_) {
|
||||
return; // Nothing to shutdown
|
||||
}
|
||||
|
||||
// Call all shutdown functions in reverse registration order
|
||||
for (auto it = shutdownFunctions_.rbegin(); it != shutdownFunctions_.rend(); ++it) {
|
||||
try {
|
||||
(*it)();
|
||||
} catch (...) {
|
||||
// Shutdown methods must be noexcept, but just in case...
|
||||
// Swallow exceptions to ensure all services get shutdown
|
||||
}
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::di
|
||||
211
src/di/service_registry.hpp
Normal file
211
src/di/service_registry.hpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
|
||||
#include "lifecycle.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl3cpp::di {
|
||||
|
||||
/**
|
||||
* @brief Manual dependency injection container (similar to Spring's ApplicationContext).
|
||||
*
|
||||
* ServiceRegistry manages service lifecycle and provides dependency injection
|
||||
* functionality. Services are registered by interface type and retrieved by
|
||||
* interface, allowing for loose coupling and testability.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* ServiceRegistry registry;
|
||||
*
|
||||
* // Register services with their dependencies
|
||||
* registry.RegisterService<IConfigService, JsonConfigService>("config.json");
|
||||
* registry.RegisterService<IWindowService, SdlWindowService>(
|
||||
* registry.GetService<IConfigService>()
|
||||
* );
|
||||
*
|
||||
* // Initialize all services in dependency order
|
||||
* registry.InitializeAll();
|
||||
*
|
||||
* // Use services
|
||||
* auto window = registry.GetService<IWindowService>();
|
||||
* window->CreateWindow({800, 600, "My App", true});
|
||||
*
|
||||
* // Shutdown all services in reverse order
|
||||
* registry.ShutdownAll();
|
||||
* @endcode
|
||||
*/
|
||||
class ServiceRegistry {
|
||||
public:
|
||||
ServiceRegistry() = default;
|
||||
~ServiceRegistry() = default;
|
||||
|
||||
// Non-copyable, non-movable
|
||||
ServiceRegistry(const ServiceRegistry&) = delete;
|
||||
ServiceRegistry& operator=(const ServiceRegistry&) = delete;
|
||||
ServiceRegistry(ServiceRegistry&&) = delete;
|
||||
ServiceRegistry& operator=(ServiceRegistry&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Register a service implementation by interface type.
|
||||
*
|
||||
* Creates an instance of Implementation and stores it as Interface.
|
||||
* All constructor arguments are forwarded to the implementation.
|
||||
*
|
||||
* If the implementation inherits from IInitializable or IShutdownable,
|
||||
* the corresponding lifecycle methods will be called during
|
||||
* InitializeAll() and ShutdownAll().
|
||||
*
|
||||
* @tparam Interface The interface type (pure virtual base class)
|
||||
* @tparam Implementation The concrete implementation type
|
||||
* @tparam Args Constructor argument types (deduced)
|
||||
* @param args Constructor arguments for the implementation
|
||||
* @throws std::runtime_error if a service of this interface type is already registered
|
||||
*/
|
||||
template<typename Interface, typename Implementation, typename... Args>
|
||||
void RegisterService(Args&&... args);
|
||||
|
||||
/**
|
||||
* @brief Get a service by interface type.
|
||||
*
|
||||
* @tparam Interface The interface type to retrieve
|
||||
* @return std::shared_ptr<Interface> Shared pointer to the service
|
||||
* @throws std::runtime_error if no service of this type is registered
|
||||
*/
|
||||
template<typename Interface>
|
||||
std::shared_ptr<Interface> GetService();
|
||||
|
||||
/**
|
||||
* @brief Get a service by interface type (const version).
|
||||
*
|
||||
* @tparam Interface The interface type to retrieve
|
||||
* @return std::shared_ptr<const Interface> Shared pointer to the service
|
||||
* @throws std::runtime_error if no service of this type is registered
|
||||
*/
|
||||
template<typename Interface>
|
||||
std::shared_ptr<const Interface> GetService() const;
|
||||
|
||||
/**
|
||||
* @brief Check if a service of the given interface type is registered.
|
||||
*
|
||||
* @tparam Interface The interface type to check
|
||||
* @return true if registered, false otherwise
|
||||
*/
|
||||
template<typename Interface>
|
||||
bool HasService() const;
|
||||
|
||||
/**
|
||||
* @brief Initialize all registered services in registration order.
|
||||
*
|
||||
* Calls Initialize() on all services that implement IInitializable.
|
||||
* Services should be registered in dependency order (dependencies first).
|
||||
*
|
||||
* @throws std::runtime_error if already initialized
|
||||
* @throws Any exception thrown by service Initialize() methods
|
||||
*/
|
||||
void InitializeAll();
|
||||
|
||||
/**
|
||||
* @brief Shutdown all registered services in reverse registration order.
|
||||
*
|
||||
* Calls Shutdown() on all services that implement IShutdownable.
|
||||
* This method does not throw exceptions (shutdown methods must be noexcept).
|
||||
*/
|
||||
void ShutdownAll() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Check if services have been initialized.
|
||||
*
|
||||
* @return true if InitializeAll() has been called, false otherwise
|
||||
*/
|
||||
bool IsInitialized() const noexcept { return initialized_; }
|
||||
|
||||
private:
|
||||
// Type-erased service storage (void* actually holds std::shared_ptr<T>)
|
||||
std::unordered_map<std::type_index, std::shared_ptr<void>> services_;
|
||||
|
||||
// Initialization functions (called in registration order)
|
||||
std::vector<std::function<void()>> initFunctions_;
|
||||
|
||||
// Shutdown functions (called in reverse registration order)
|
||||
std::vector<std::function<void()>> shutdownFunctions_;
|
||||
|
||||
// Initialization state
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// Template implementation
|
||||
|
||||
template<typename Interface, typename Implementation, typename... Args>
|
||||
void ServiceRegistry::RegisterService(Args&&... args) {
|
||||
const std::type_index typeIndex(typeid(Interface));
|
||||
|
||||
// Check if already registered
|
||||
if (services_.find(typeIndex) != services_.end()) {
|
||||
throw std::runtime_error(
|
||||
std::string("Service already registered: ") + typeid(Interface).name()
|
||||
);
|
||||
}
|
||||
|
||||
// Create the implementation instance
|
||||
auto implementation = std::make_shared<Implementation>(std::forward<Args>(args)...);
|
||||
|
||||
// Store as interface type (type-erased as void*)
|
||||
services_[typeIndex] = std::static_pointer_cast<void>(
|
||||
std::static_pointer_cast<Interface>(implementation)
|
||||
);
|
||||
|
||||
// Register initialization if implements IInitializable
|
||||
if (auto initializable = std::dynamic_pointer_cast<IInitializable>(implementation)) {
|
||||
initFunctions_.push_back([initializable]() {
|
||||
initializable->Initialize();
|
||||
});
|
||||
}
|
||||
|
||||
// Register shutdown if implements IShutdownable
|
||||
if (auto shutdownable = std::dynamic_pointer_cast<IShutdownable>(implementation)) {
|
||||
shutdownFunctions_.push_back([shutdownable]() {
|
||||
shutdownable->Shutdown();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Interface>
|
||||
std::shared_ptr<Interface> ServiceRegistry::GetService() {
|
||||
const std::type_index typeIndex(typeid(Interface));
|
||||
|
||||
auto it = services_.find(typeIndex);
|
||||
if (it == services_.end()) {
|
||||
throw std::runtime_error(
|
||||
std::string("Service not found: ") + typeid(Interface).name()
|
||||
);
|
||||
}
|
||||
|
||||
return std::static_pointer_cast<Interface>(it->second);
|
||||
}
|
||||
|
||||
template<typename Interface>
|
||||
std::shared_ptr<const Interface> ServiceRegistry::GetService() const {
|
||||
const std::type_index typeIndex(typeid(Interface));
|
||||
|
||||
auto it = services_.find(typeIndex);
|
||||
if (it == services_.end()) {
|
||||
throw std::runtime_error(
|
||||
std::string("Service not found: ") + typeid(Interface).name()
|
||||
);
|
||||
}
|
||||
|
||||
return std::static_pointer_cast<const Interface>(it->second);
|
||||
}
|
||||
|
||||
template<typename Interface>
|
||||
bool ServiceRegistry::HasService() const {
|
||||
const std::type_index typeIndex(typeid(Interface));
|
||||
return services_.find(typeIndex) != services_.end();
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::di
|
||||
66
src/events/event_bus.cpp
Normal file
66
src/events/event_bus.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "event_bus.hpp"
|
||||
|
||||
namespace sdl3cpp::events {
|
||||
|
||||
void EventBus::Subscribe(EventType type, EventListener listener) {
|
||||
listeners_[type].push_back(std::move(listener));
|
||||
}
|
||||
|
||||
void EventBus::SubscribeAll(EventListener listener) {
|
||||
globalListeners_.push_back(std::move(listener));
|
||||
}
|
||||
|
||||
void EventBus::Publish(const Event& event) {
|
||||
DispatchEvent(event);
|
||||
}
|
||||
|
||||
void EventBus::PublishAsync(const Event& event) {
|
||||
std::lock_guard<std::mutex> lock(queueMutex_);
|
||||
eventQueue_.push(event);
|
||||
}
|
||||
|
||||
void EventBus::ProcessQueue() {
|
||||
// Lock to swap the queue (minimize lock time)
|
||||
std::queue<Event> localQueue;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queueMutex_);
|
||||
localQueue.swap(eventQueue_);
|
||||
}
|
||||
|
||||
// Process all queued events without holding the lock
|
||||
while (!localQueue.empty()) {
|
||||
DispatchEvent(localQueue.front());
|
||||
localQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void EventBus::ClearListeners() {
|
||||
listeners_.clear();
|
||||
globalListeners_.clear();
|
||||
}
|
||||
|
||||
size_t EventBus::GetListenerCount(EventType type) const {
|
||||
auto it = listeners_.find(type);
|
||||
return it != listeners_.end() ? it->second.size() : 0;
|
||||
}
|
||||
|
||||
size_t EventBus::GetGlobalListenerCount() const {
|
||||
return globalListeners_.size();
|
||||
}
|
||||
|
||||
void EventBus::DispatchEvent(const Event& event) {
|
||||
// Dispatch to type-specific listeners
|
||||
auto it = listeners_.find(event.type);
|
||||
if (it != listeners_.end()) {
|
||||
for (const auto& listener : it->second) {
|
||||
listener(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch to global listeners
|
||||
for (const auto& listener : globalListeners_) {
|
||||
listener(event);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::events
|
||||
149
src/events/event_bus.hpp
Normal file
149
src/events/event_bus.hpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include "event_listener.hpp"
|
||||
#include "event_types.hpp"
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl3cpp::events {
|
||||
|
||||
/**
|
||||
* @brief Event bus for decoupled component communication.
|
||||
*
|
||||
* Similar to Spring's ApplicationEventPublisher, the EventBus allows
|
||||
* services to publish events and subscribe to events without direct
|
||||
* dependencies on each other.
|
||||
*
|
||||
* The event bus supports both synchronous and asynchronous event publishing:
|
||||
* - Publish(): Immediately invokes all listeners (useful for critical events)
|
||||
* - PublishAsync(): Queues event for next ProcessQueue() call (useful for cross-thread events)
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* EventBus eventBus;
|
||||
*
|
||||
* // Subscribe to specific event type
|
||||
* eventBus.Subscribe(EventType::KeyPressed, [](const Event& event) {
|
||||
* auto keyEvent = event.GetData<KeyEvent>();
|
||||
* std::cout << "Key: " << keyEvent.key << std::endl;
|
||||
* });
|
||||
*
|
||||
* // Publish event synchronously
|
||||
* KeyEvent data{SDLK_SPACE, SDL_SCANCODE_SPACE, SDL_KMOD_NONE, false};
|
||||
* eventBus.Publish(Event{EventType::KeyPressed, 0.0, data});
|
||||
*
|
||||
* // Or publish asynchronously (queued)
|
||||
* eventBus.PublishAsync(Event{EventType::KeyPressed, 0.0, data});
|
||||
* eventBus.ProcessQueue(); // Call once per frame
|
||||
* @endcode
|
||||
*/
|
||||
class EventBus {
|
||||
public:
|
||||
EventBus() = default;
|
||||
~EventBus() = default;
|
||||
|
||||
// Non-copyable, non-movable
|
||||
EventBus(const EventBus&) = delete;
|
||||
EventBus& operator=(const EventBus&) = delete;
|
||||
EventBus(EventBus&&) = delete;
|
||||
EventBus& operator=(EventBus&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Subscribe to a specific event type.
|
||||
*
|
||||
* The listener will be called whenever an event of the specified type
|
||||
* is published (either via Publish() or PublishAsync()).
|
||||
*
|
||||
* @param type The event type to subscribe to
|
||||
* @param listener The callback function to invoke
|
||||
*/
|
||||
void Subscribe(EventType type, EventListener listener);
|
||||
|
||||
/**
|
||||
* @brief Subscribe to all event types.
|
||||
*
|
||||
* The listener will be called for every event published, regardless of type.
|
||||
* Useful for logging, debugging, or telemetry.
|
||||
*
|
||||
* @param listener The callback function to invoke for all events
|
||||
*/
|
||||
void SubscribeAll(EventListener listener);
|
||||
|
||||
/**
|
||||
* @brief Publish an event synchronously.
|
||||
*
|
||||
* Immediately invokes all listeners subscribed to this event type,
|
||||
* as well as all global listeners. This blocks until all listeners complete.
|
||||
*
|
||||
* Use this for critical events that must be processed immediately
|
||||
* (e.g., window resize, shutdown requests).
|
||||
*
|
||||
* @param event The event to publish
|
||||
*/
|
||||
void Publish(const Event& event);
|
||||
|
||||
/**
|
||||
* @brief Publish an event asynchronously.
|
||||
*
|
||||
* Queues the event for later processing. The event will be dispatched
|
||||
* to listeners when ProcessQueue() is called.
|
||||
*
|
||||
* Use this for non-critical events or when publishing from a different
|
||||
* thread (e.g., audio thread, network thread).
|
||||
*
|
||||
* @param event The event to publish
|
||||
*/
|
||||
void PublishAsync(const Event& event);
|
||||
|
||||
/**
|
||||
* @brief Process all queued asynchronous events.
|
||||
*
|
||||
* Dispatches all events queued via PublishAsync() to their subscribers.
|
||||
* This should be called once per frame in the main loop.
|
||||
*
|
||||
* Thread-safe: Can be called while other threads are calling PublishAsync().
|
||||
*/
|
||||
void ProcessQueue();
|
||||
|
||||
/**
|
||||
* @brief Remove all event listeners.
|
||||
*
|
||||
* Useful for testing or resetting the event bus state.
|
||||
*/
|
||||
void ClearListeners();
|
||||
|
||||
/**
|
||||
* @brief Get the number of listeners for a specific event type.
|
||||
*
|
||||
* @param type The event type to query
|
||||
* @return The number of listeners subscribed to this event type
|
||||
*/
|
||||
size_t GetListenerCount(EventType type) const;
|
||||
|
||||
/**
|
||||
* @brief Get the number of global listeners.
|
||||
*
|
||||
* @return The number of listeners subscribed to all events
|
||||
*/
|
||||
size_t GetGlobalListenerCount() const;
|
||||
|
||||
private:
|
||||
// Event type -> list of listeners
|
||||
std::unordered_map<EventType, std::vector<EventListener>> listeners_;
|
||||
|
||||
// Listeners that receive all events
|
||||
std::vector<EventListener> globalListeners_;
|
||||
|
||||
// Queue for asynchronous events
|
||||
std::queue<Event> eventQueue_;
|
||||
|
||||
// Mutex to protect eventQueue_ (allows cross-thread PublishAsync)
|
||||
mutable std::mutex queueMutex_;
|
||||
|
||||
// Helper to dispatch event to listeners
|
||||
void DispatchEvent(const Event& event);
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::events
|
||||
28
src/events/event_listener.hpp
Normal file
28
src/events/event_listener.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace sdl3cpp::events {
|
||||
|
||||
// Forward declaration
|
||||
struct Event;
|
||||
|
||||
/**
|
||||
* @brief Type alias for event listener callbacks.
|
||||
*
|
||||
* Event listeners are functions that receive an Event and process it.
|
||||
* Similar to Spring's @EventListener annotation, but as a function type.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* EventListener listener = [](const Event& event) {
|
||||
* if (event.type == EventType::KeyPressed) {
|
||||
* auto keyEvent = event.GetData<KeyEvent>();
|
||||
* std::cout << "Key pressed: " << keyEvent.key << std::endl;
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
using EventListener = std::function<void(const Event&)>;
|
||||
|
||||
} // namespace sdl3cpp::events
|
||||
208
src/events/event_types.hpp
Normal file
208
src/events/event_types.hpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include <any>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace sdl3cpp::events {
|
||||
|
||||
/**
|
||||
* @brief Event type enumeration.
|
||||
*
|
||||
* Defines all event types that can be published on the event bus.
|
||||
* Similar to Spring's ApplicationEvent hierarchy, but using an enum
|
||||
* with type-erased data instead of inheritance.
|
||||
*/
|
||||
enum class EventType {
|
||||
// Window events
|
||||
WindowResized,
|
||||
WindowClosed,
|
||||
WindowMinimized,
|
||||
WindowMaximized,
|
||||
WindowRestored,
|
||||
WindowFocusGained,
|
||||
WindowFocusLost,
|
||||
|
||||
// Input events
|
||||
KeyPressed,
|
||||
KeyReleased,
|
||||
MouseMoved,
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseWheel,
|
||||
TextInput,
|
||||
|
||||
// Rendering events
|
||||
FrameBegin,
|
||||
FrameEnd,
|
||||
SwapchainRecreated,
|
||||
RenderError,
|
||||
|
||||
// Audio events
|
||||
AudioPlayRequested,
|
||||
AudioStopped,
|
||||
AudioError,
|
||||
|
||||
// Script events
|
||||
ScriptLoaded,
|
||||
ScriptError,
|
||||
SceneLoaded,
|
||||
|
||||
// Physics events
|
||||
PhysicsStepComplete,
|
||||
CollisionDetected,
|
||||
|
||||
// Application lifecycle events
|
||||
ApplicationStarted,
|
||||
ApplicationShutdown,
|
||||
ApplicationPaused,
|
||||
ApplicationResumed,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base event structure.
|
||||
*
|
||||
* Contains event type, timestamp, and type-erased data payload.
|
||||
* Services publish events and subscribers retrieve typed data using GetData<T>().
|
||||
*/
|
||||
struct Event {
|
||||
EventType type;
|
||||
double timestamp; // Seconds since application start
|
||||
std::any data; // Type-erased payload
|
||||
|
||||
/**
|
||||
* @brief Retrieve typed data from the event.
|
||||
*
|
||||
* @tparam T The expected data type
|
||||
* @return const T& Reference to the data
|
||||
* @throws std::bad_any_cast if data is not of type T
|
||||
*/
|
||||
template<typename T>
|
||||
const T& GetData() const {
|
||||
return std::any_cast<const T&>(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if event contains data of a specific type.
|
||||
*
|
||||
* @tparam T The type to check for
|
||||
* @return true if data is of type T, false otherwise
|
||||
*/
|
||||
template<typename T>
|
||||
bool HasData() const {
|
||||
return data.type() == typeid(T);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Event Data Structures
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Window resize event data.
|
||||
*/
|
||||
struct WindowResizedEvent {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Key press/release event data.
|
||||
*/
|
||||
struct KeyEvent {
|
||||
SDL_Keycode key;
|
||||
SDL_Scancode scancode;
|
||||
SDL_Keymod modifiers;
|
||||
bool repeat;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mouse movement event data.
|
||||
*/
|
||||
struct MouseMovedEvent {
|
||||
float x;
|
||||
float y;
|
||||
float deltaX;
|
||||
float deltaY;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mouse button press/release event data.
|
||||
*/
|
||||
struct MouseButtonEvent {
|
||||
uint8_t button;
|
||||
uint8_t clicks;
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mouse wheel event data.
|
||||
*/
|
||||
struct MouseWheelEvent {
|
||||
float deltaX;
|
||||
float deltaY;
|
||||
bool flipped;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Text input event data.
|
||||
*/
|
||||
struct TextInputEvent {
|
||||
std::string text;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Frame timing event data.
|
||||
*/
|
||||
struct FrameEvent {
|
||||
uint64_t frameNumber;
|
||||
double deltaTime; // Seconds since last frame
|
||||
double totalTime; // Seconds since application start
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Swapchain recreation event data.
|
||||
*/
|
||||
struct SwapchainRecreatedEvent {
|
||||
uint32_t newWidth;
|
||||
uint32_t newHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Error event data.
|
||||
*/
|
||||
struct ErrorEvent {
|
||||
std::string message;
|
||||
std::string component;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Audio playback event data.
|
||||
*/
|
||||
struct AudioPlayEvent {
|
||||
std::string filePath;
|
||||
bool loop;
|
||||
bool background; // true for music, false for sound effects
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Script loaded event data.
|
||||
*/
|
||||
struct ScriptLoadedEvent {
|
||||
std::string scriptPath;
|
||||
bool debugMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Collision detection event data.
|
||||
*/
|
||||
struct CollisionEvent {
|
||||
std::string objectA;
|
||||
std::string objectB;
|
||||
float impactForce;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::events
|
||||
Reference in New Issue
Block a user