aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------lib/sdl20
m---------lib/sdl_image0
m---------lib/sdl_ttf0
-rw-r--r--src/crepe/api/CMakeLists.txt11
-rw-r--r--src/crepe/api/Event.h112
-rw-r--r--src/crepe/api/EventHandler.cpp5
-rw-r--r--src/crepe/api/EventHandler.h96
-rw-r--r--src/crepe/api/EventHandler.hpp18
-rw-r--r--src/crepe/api/EventManager.cpp54
-rw-r--r--src/crepe/api/EventManager.h142
-rw-r--r--src/crepe/api/EventManager.hpp48
-rw-r--r--src/crepe/api/IKeyListener.cpp18
-rw-r--r--src/crepe/api/IKeyListener.h49
-rw-r--r--src/crepe/api/IMouseListener.cpp28
-rw-r--r--src/crepe/api/IMouseListener.h72
-rw-r--r--src/crepe/api/KeyCodes.h233
-rw-r--r--src/crepe/facade/SDLContext.cpp1
-rw-r--r--src/example/CMakeLists.txt6
-rw-r--r--src/test/CMakeLists.txt1
-rw-r--r--src/test/EventTest.cpp254
20 files changed, 1147 insertions, 1 deletions
diff --git a/lib/sdl2 b/lib/sdl2
-Subproject 9519b9916cd29a14587af0507292f2bd31dd575
+Subproject c98c4fbff6d8f3016a3ce6685bf8f43433c3efc
diff --git a/lib/sdl_image b/lib/sdl_image
-Subproject abcf63aa71b4e3ac32120fa9870a6500ddcdcc8
+Subproject 56560190a6c5b5740e5afdce747e2a2bfbf16db
diff --git a/lib/sdl_ttf b/lib/sdl_ttf
-Subproject 4a318f8dfaa1bb6f10e0c5e54052e25d3c7f344
+Subproject a3d0895c1b60c41ff9e85d9203ddd7485c014da
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index f9b370f..a185ca6 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -17,8 +17,12 @@ target_sources(crepe PUBLIC
Vector2.cpp
Camera.cpp
Animator.cpp
+ EventManager.cpp
+ IKeyListener.cpp
+ IMouseListener.cpp
LoopManager.cpp
LoopTimer.cpp
+ EventHandler.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -43,6 +47,13 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SceneManager.hpp
Camera.h
Animator.h
+ EventManager.h
+ EventManager.hpp
+ EventHandler.h
+ EventHandler.hpp
+ Event.h
+ IKeyListener.h
+ IMouseListener.h
LoopManager.h
LoopTimer.h
)
diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h
new file mode 100644
index 0000000..06cf7f3
--- /dev/null
+++ b/src/crepe/api/Event.h
@@ -0,0 +1,112 @@
+// TODO discussing the location of these events
+#pragma once
+
+#include <string>
+
+#include "KeyCodes.h"
+
+/**
+ * \brief Base class for all event types in the system.
+ */
+class Event {};
+
+/**
+ * \brief Event triggered when a key is pressed.
+ */
+class KeyPressEvent : public Event {
+public:
+ //! false if first time press, true if key is repeated
+ bool repeat = false;
+
+ //! The key that was pressed.
+ Keycode key = Keycode::NONE;
+};
+
+/**
+ * \brief Event triggered when a key is released.
+ */
+class KeyReleaseEvent : public Event {
+public:
+ //! The key that was released.
+ Keycode key = Keycode::NONE;
+};
+
+/**
+ * \brief Event triggered when a mouse button is pressed.
+ */
+class MousePressEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+
+ //! The mouse button that was pressed.
+ MouseButton button = MouseButton::NONE;
+};
+
+/**
+ * \brief Event triggered when a mouse button is clicked (press and release).
+ */
+class MouseClickEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+
+ //! The mouse button that was clicked.
+ MouseButton button = MouseButton::NONE;
+};
+
+/**
+ * \brief Event triggered when a mouse button is released.
+ */
+class MouseReleaseEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+
+ //! The mouse button that was released.
+ MouseButton button = MouseButton::NONE;
+};
+
+/**
+ * \brief Event triggered when the mouse is moved.
+ */
+class MouseMoveEvent : public Event {
+public:
+ //! X-coordinate of the mouse position at the time of the event.
+ int mouse_x = 0;
+
+ //! Y-coordinate of the mouse position at the time of the event.
+ int mouse_y = 0;
+};
+
+/**
+ * \brief Event triggered during a collision between objects.
+ */
+class CollisionEvent : public Event {
+public:
+ //! Data describing the collision (currently not implemented).
+ // Collision collisionData;
+};
+
+/**
+ * \brief Event triggered when text is submitted, e.g., from a text input.
+ */
+class TextSubmitEvent : public Event {
+public:
+ //! The submitted text.
+ std::string text = "";
+};
+
+/**
+ * \brief Event triggered to indicate the application is shutting down.
+ */
+class ShutDownEvent : public Event {};
diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp
new file mode 100644
index 0000000..4dc232f
--- /dev/null
+++ b/src/crepe/api/EventHandler.cpp
@@ -0,0 +1,5 @@
+#include "EventHandler.h"
+
+using namespace crepe;
+
+bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); }
diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h
new file mode 100644
index 0000000..ef659fd
--- /dev/null
+++ b/src/crepe/api/EventHandler.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <functional>
+#include <string>
+
+#include "Event.h"
+
+namespace crepe {
+/**
+ * \brief A type alias for an event handler function.
+ *
+ * The EventHandler is a std::function that takes an EventType reference and returns a boolean value
+ * indicating whether the event is handled.
+ *
+ * \tparam EventType The type of event this handler will handle.
+ *
+ * Returning \c false from an event handler results in the event being propogated to other listeners for the same event type, while returning \c true stops propogation altogether.
+ */
+template <typename EventType>
+using EventHandler = std::function<bool(const EventType & e)>;
+
+/**
+ * \class IEventHandlerWrapper
+ * \brief An abstract base class for event handler wrappers.
+ *
+ * This class provides the interface for handling events. Derived classes must implement the
+ * `call()` method to process events
+ */
+class IEventHandlerWrapper {
+public:
+ /**
+ * \brief Virtual destructor for IEventHandlerWrapper.
+ */
+ virtual ~IEventHandlerWrapper() = default;
+
+ /**
+ * \brief Executes the handler with the given event.
+ *
+ * This method calls the `call()` method of the derived class, passing the event to the handler.
+ *
+ * \param e The event to be processed.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ bool exec(const Event & e);
+
+private:
+ /**
+ * \brief The method responsible for handling the event.
+ *
+ * This method is implemented by derived classes to process the event.
+ *
+ * \param e The event to be processed.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ virtual bool call(const Event & e) = 0;
+};
+
+/**
+ * \class EventHandlerWrapper
+ * \brief A wrapper for event handler functions.
+ *
+ * This class wraps an event handler function of a specific event type. It implements the
+ * `call()` and `get_type()` methods to allow the handler to be executed and its type to be
+ * queried.
+ *
+ * \tparam EventType The type of event this handler will handle.
+ */
+template <typename EventType>
+class EventHandlerWrapper : public IEventHandlerWrapper {
+public:
+ /**
+ * \brief Constructs an EventHandlerWrapper with a given handler.
+ *
+ * The constructor takes an event handler function and stores it in the wrapper.
+ *
+ * \param handler The event handler function.
+ */
+ explicit EventHandlerWrapper(const EventHandler<EventType> & handler);
+
+private:
+ /**
+ * \brief Calls the stored event handler with the event.
+ *
+ * This method casts the event to the appropriate type and calls the handler.
+ *
+ * \param e The event to be handled.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ bool call(const Event & e) override;
+ //! The event handler function.
+ EventHandler<EventType> handler;
+};
+
+} // namespace crepe
+
+#include "EventHandler.hpp"
diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp
new file mode 100644
index 0000000..391dcca
--- /dev/null
+++ b/src/crepe/api/EventHandler.hpp
@@ -0,0 +1,18 @@
+#include <typeindex>
+
+#include "EventHandler.h"
+
+namespace crepe {
+
+// Implementation of EventHandlerWrapper constructor
+template <typename EventType>
+EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler)
+ : handler(handler) {}
+
+// Implementation of EventHandlerWrapper::call
+template <typename EventType>
+bool EventHandlerWrapper<EventType>::call(const Event & e) {
+ return this->handler(static_cast<const EventType &>(e));
+}
+
+} //namespace crepe
diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp
new file mode 100644
index 0000000..993db86
--- /dev/null
+++ b/src/crepe/api/EventManager.cpp
@@ -0,0 +1,54 @@
+#include "EventManager.h"
+
+using namespace crepe;
+
+EventManager & EventManager::get_instance() {
+ static EventManager instance;
+ return instance;
+}
+
+void EventManager::dispatch_events() {
+ for (auto event_it = this->events_queue.begin(); event_it != this->events_queue.end();) {
+ std::unique_ptr<Event> & event = (*event_it).event;
+ int channel = (*event_it).channel;
+ std::type_index event_type = (*event_it).type;
+
+ bool event_handled = false;
+ auto handlers_it = this->subscribers.find(event_type);
+ if (handlers_it == this->subscribers.end()) {
+ continue;
+ }
+ std::vector<CallbackEntry> & handlers = handlers_it->second;
+
+ for (auto handler_it = handlers.begin(); handler_it != handlers.end(); ++handler_it) {
+ // If callback is executed and returns true, remove the event from the queue
+ if ((*handler_it).callback->exec(*event)) {
+ event_it = this->events_queue.erase(event_it);
+ event_handled = true;
+ break;
+ }
+ }
+
+ if (!event_handled) {
+ ++event_it;
+ }
+ }
+}
+
+void EventManager::clear() {
+ this->subscribers.clear();
+ this->events_queue.clear();
+}
+
+void EventManager::unsubscribe(subscription_t event_id) {
+ for (auto & [event_type, handlers] : this->subscribers) {
+ for (auto it = handlers.begin(); it != handlers.end();) {
+ if (it->id == event_id) {
+ it = handlers.erase(it);
+ return;
+ } else {
+ ++it;
+ }
+ }
+ }
+}
diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h
new file mode 100644
index 0000000..bd9772a
--- /dev/null
+++ b/src/crepe/api/EventManager.h
@@ -0,0 +1,142 @@
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#include "Event.h"
+#include "EventHandler.h"
+
+namespace crepe {
+//! typedef for subscription value
+typedef int subscription_t;
+
+/**
+ * \class EventManager
+ * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling.
+ *
+ * The `EventManager` acts as a centralized event system. It allows for registering callbacks
+ * for specific event types, triggering events synchronously, queueing events for later
+ * processing, and managing subscriptions via unique identifiers.
+ */
+class EventManager {
+public:
+ static constexpr int CHANNEL_ALL = -1;
+
+ /**
+ * \brief Get the singleton instance of the EventManager.
+ *
+ * This method returns the unique instance of the EventManager, creating it if it
+ * doesn't already exist. Ensures only one instance is active in the program.
+ *
+ * \return Reference to the singleton instance of the EventManager.
+ */
+ static EventManager & get_instance();
+
+ /**
+ * \brief Subscribe to a specific event type.
+ *
+ * Registers a callback for a given event type and optional channel. Each callback
+ * is assigned a unique subscription ID that can be used for later unsubscription.
+ *
+ * \tparam EventType The type of the event to subscribe to.
+ * \param callback The callback function to be invoked when the event is triggered.
+ * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels).
+ * \return A unique subscription ID associated with the registered callback.
+ */
+ template <typename EventType>
+ subscription_t subscribe(const EventHandler<EventType> & callback,
+ int channel = CHANNEL_ALL);
+
+ /**
+ * \brief Unsubscribe a previously registered callback.
+ *
+ * Removes a callback from the subscription list based on its unique subscription ID.
+ *
+ * \param event_id The unique subscription ID of the callback to remove.
+ */
+ void unsubscribe(subscription_t event_id);
+
+ /**
+ * \brief Trigger an event immediately.
+ *
+ * Synchronously invokes all registered callbacks for the given event type on the specified channel.
+ *
+ * \tparam EventType The type of the event to trigger.
+ * \param event The event instance to pass to the callbacks.
+ * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels).
+ */
+ template <typename EventType>
+ void trigger_event(const EventType & event, int channel = CHANNEL_ALL);
+
+ /**
+ * \brief Queue an event for later processing.
+ *
+ * Adds an event to the event queue to be processed during the next call to `dispatch_events`.
+ *
+ * \tparam EventType The type of the event to queue.
+ * \param event The event instance to queue.
+ * \param channel The channel to associate with the event (default is CHANNEL_ALL).
+ */
+ template <typename EventType>
+ void queue_event(const EventType & event, int channel = CHANNEL_ALL);
+
+ /**
+ * \brief Process all queued events.
+ *
+ * Iterates through the event queue and triggers callbacks for each queued event.
+ * Events are removed from the queue once processed.
+ */
+ void dispatch_events();
+
+ /**
+ * \brief Clear all subscriptions.
+ *
+ * Removes all registered event handlers and clears the subscription list.
+ */
+ void clear();
+
+private:
+ /**
+ * \brief Default constructor for the EventManager.
+ *
+ * Constructor is private to enforce the singleton pattern.
+ */
+ EventManager() = default;
+
+ /**
+ * \struct QueueEntry
+ * \brief Represents an entry in the event queue.
+ */
+ struct QueueEntry {
+ std::unique_ptr<Event> event; ///< The event instance.
+ int channel = CHANNEL_ALL; ///< The channel associated with the event.
+ std::type_index type; ///< The type of the event.
+ };
+
+ /**
+ * \struct CallbackEntry
+ * \brief Represents a registered event handler callback.
+ */
+ struct CallbackEntry {
+ std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper.
+ int channel = CHANNEL_ALL; ///< The channel this callback listens to.
+ subscription_t id = -1; ///< Unique subscription ID.
+ };
+
+ //! The queue of events to be processed during dispatch.
+ std::vector<QueueEntry> events_queue;
+
+ //! A map of event type to registered callbacks.
+ std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers;
+
+ //! Counter to generate unique subscription IDs.
+ subscription_t subscription_counter = 0;
+};
+
+} // namespace crepe
+
+#include "EventManager.hpp"
diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp
new file mode 100644
index 0000000..b2a94bd
--- /dev/null
+++ b/src/crepe/api/EventManager.hpp
@@ -0,0 +1,48 @@
+#include "EventManager.h"
+
+namespace crepe {
+
+template <typename EventType>
+subscription_t EventManager::subscribe(const EventHandler<EventType> & callback, int channel) {
+ subscription_counter++;
+ std::type_index event_type = typeid(EventType);
+ std::unique_ptr<EventHandlerWrapper<EventType>> handler
+ = std::make_unique<EventHandlerWrapper<EventType>>(callback);
+ std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
+ handlers.emplace_back(CallbackEntry{
+ .callback = std::move(handler), .channel = channel, .id = subscription_counter});
+ return subscription_counter;
+}
+
+template <typename EventType>
+void EventManager::queue_event(const EventType & event, int channel) {
+ static_assert(std::is_base_of<Event, EventType>::value,
+ "EventType must derive from Event");
+ std::type_index event_type = typeid(EventType);
+
+ auto event_ptr = std::make_unique<EventType>(event);
+
+ this->events_queue.push_back(
+ QueueEntry{.event = std::move(event_ptr), .channel = channel, .type = event_type});
+}
+
+template <typename EventType>
+void EventManager::trigger_event(const EventType & event, int channel) {
+ std::type_index event_type = typeid(EventType);
+
+ auto handlers_it = this->subscribers.find(event_type);
+ if (handlers_it != this->subscribers.end()) {
+ const std::vector<CallbackEntry> & handlers = handlers_it->second;
+
+ for (const CallbackEntry & handler : handlers) {
+ if (handler.channel != channel && handler.channel != CHANNEL_ALL) {
+ continue;
+ }
+ if (handler.callback->exec(event)) {
+ break;
+ }
+ }
+ }
+}
+
+} // namespace crepe
diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp
new file mode 100644
index 0000000..7aefaf7
--- /dev/null
+++ b/src/crepe/api/IKeyListener.cpp
@@ -0,0 +1,18 @@
+#include "IKeyListener.h"
+
+using namespace crepe;
+
+// Constructor with specified channel
+IKeyListener::IKeyListener(int channel) : event_manager(EventManager::get_instance()) {
+ this->press_id = event_manager.subscribe<KeyPressEvent>(
+ [this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel);
+ this->release_id = event_manager.subscribe<KeyReleaseEvent>(
+ [this](const KeyReleaseEvent & event) { return this->on_key_released(event); },
+ channel);
+}
+
+// Destructor, unsubscribe events
+IKeyListener::~IKeyListener() {
+ event_manager.unsubscribe(this->press_id);
+ event_manager.unsubscribe(this->release_id);
+}
diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h
new file mode 100644
index 0000000..2a89cbc
--- /dev/null
+++ b/src/crepe/api/IKeyListener.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventManager.h"
+
+namespace crepe {
+
+/**
+ * \class IKeyListener
+ * \brief Interface for keyboard event handling in the application.
+ */
+class IKeyListener {
+public:
+ /**
+ * \brief Constructs an IKeyListener with a specified channel.
+ * \param channel The channel ID for event handling.
+ */
+ IKeyListener(int channel = EventManager::CHANNEL_ALL);
+ virtual ~IKeyListener();
+ IKeyListener(const IKeyListener &) = delete;
+ IKeyListener & operator=(const IKeyListener &) = delete;
+ IKeyListener & operator=(IKeyListener &&) = delete;
+ IKeyListener(IKeyListener &&) = delete;
+
+ /**
+ * \brief Pure virtual function to handle key press events.
+ * \param event The key press event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_key_pressed(const KeyPressEvent & event) = 0;
+
+ /**
+ * \brief Pure virtual function to handle key release events.
+ * \param event The key release event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_key_released(const KeyReleaseEvent & event) = 0;
+
+private:
+ //! Key press event id
+ subscription_t press_id = -1;
+ //! Key release event id
+ subscription_t release_id = -1;
+ //! EventManager reference
+ EventManager & event_manager;;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp
new file mode 100644
index 0000000..7d38280
--- /dev/null
+++ b/src/crepe/api/IMouseListener.cpp
@@ -0,0 +1,28 @@
+#include "IMouseListener.h"
+
+using namespace crepe;
+
+IMouseListener::IMouseListener(int channel) : event_manager(EventManager::get_instance()) {
+ this->click_id = event_manager.subscribe<MouseClickEvent>(
+ [this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); },
+ channel);
+
+ this->press_id = event_manager.subscribe<MousePressEvent>(
+ [this](const MousePressEvent & event) { return this->on_mouse_pressed(event); },
+ channel);
+
+ this->release_id = event_manager.subscribe<MouseReleaseEvent>(
+ [this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); },
+ channel);
+
+ this->move_id = event_manager.subscribe<MouseMoveEvent>(
+ [this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel);
+}
+
+IMouseListener::~IMouseListener() {
+ // Unsubscribe event handlers
+ event_manager.unsubscribe(this->click_id);
+ event_manager.unsubscribe(this->press_id);
+ event_manager.unsubscribe(this->release_id);
+ event_manager.unsubscribe(this->move_id);
+}
diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h
new file mode 100644
index 0000000..91b33e1
--- /dev/null
+++ b/src/crepe/api/IMouseListener.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventManager.h"
+
+namespace crepe {
+
+/**
+ * \class IMouseListener
+ * \brief Interface for mouse event handling in the application.
+ */
+class IMouseListener {
+public:
+ /**
+ * \brief Constructs an IMouseListener with a specified channel.
+ * \param channel The channel ID for event handling.
+ */
+ IMouseListener(int channel = EventManager::CHANNEL_ALL);
+ virtual ~IMouseListener();
+ IMouseListener & operator=(const IMouseListener &) = delete;
+ IMouseListener(const IMouseListener &) = delete;
+ IMouseListener & operator=(const IMouseListener &&) = delete;
+ IMouseListener(IMouseListener &&) = delete;
+
+ /**
+ * \brief Move assignment operator (deleted).
+ */
+ IMouseListener & operator=(IMouseListener &&) = delete;
+
+ /**
+ * \brief Handles a mouse click event.
+ * \param event The mouse click event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse press event.
+ * \param event The mouse press event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_pressed(const MousePressEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse release event.
+ * \param event The mouse release event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse move event.
+ * \param event The mouse move event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0;
+
+private:
+ //! Mouse click event id
+ subscription_t click_id = -1;
+ //! Mouse press event id
+ subscription_t press_id = -1;
+ //! Mouse release event id
+ subscription_t release_id = -1;
+ //! Mouse move event id
+ subscription_t move_id = -1;
+ //! EventManager reference
+ EventManager & event_manager;
+};
+
+} //namespace crepe
diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h
new file mode 100644
index 0000000..16c2108
--- /dev/null
+++ b/src/crepe/api/KeyCodes.h
@@ -0,0 +1,233 @@
+#pragma once
+
+//! Enumeration for mouse button inputs, including standard and extended buttons.
+enum class MouseButton {
+ //! No mouse button input.
+ NONE = 0,
+ //! Left mouse button.
+ LEFT_MOUSE = 1,
+ //! Right mouse button.
+ RIGHT_MOUSE = 2,
+ //! Middle mouse button (scroll wheel press).
+ MIDDLE_MOUSE = 3,
+ //! First extended mouse button.
+ X1_MOUSE = 4,
+ //! Second extended mouse button.
+ X2_MOUSE = 5,
+ //! Scroll wheel upward movement.
+ SCROLL_UP = 6,
+ //! Scroll wheel downward movement.
+ SCROLL_DOWN = 7,
+};
+
+//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys.
+enum class Keycode {
+ //! No key input.
+ NONE = 0,
+ //! Spacebar.
+ SPACE = 32,
+ //! Apostrophe (').
+ APOSTROPHE = 39,
+ //! Comma (,).
+ COMMA = 44,
+ //! Minus (-).
+ MINUS = 45,
+ //! Period (.).
+ PERIOD = 46,
+ //! Slash (/).
+ SLASH = 47,
+ //! Digit 0.
+ D0 = 48,
+ //! Digit 1.
+ D1 = 49,
+ //! Digit 2.
+ D2 = 50,
+ //! Digit 3.
+ D3 = 51,
+ //! Digit 4.
+ D4 = 52,
+ //! Digit 5.
+ D5 = 53,
+ //! Digit 6.
+ D6 = 54,
+ //! Digit 7.
+ D7 = 55,
+ //! Digit 8.
+ D8 = 56,
+ //! Digit 9.
+ D9 = 57,
+ //! Semicolon (;).
+ SEMICOLON = 59,
+ //! Equal sign (=).
+ EQUAL = 61,
+ //! Key 'A'.
+ A = 65,
+ //! Key 'B'.
+ B = 66,
+ //! Key 'C'.
+ C = 67,
+ //! Key 'D'.
+ D = 68,
+ //! Key 'E'.
+ E = 69,
+ //! Key 'F'.
+ F = 70,
+ //! Key 'G'.
+ G = 71,
+ //! Key 'H'.
+ H = 72,
+ //! Key 'I'.
+ I = 73,
+ //! Key 'J'.
+ J = 74,
+ //! Key 'K'.
+ K = 75,
+ //! Key 'L'.
+ L = 76,
+ //! Key 'M'.
+ M = 77,
+ //! Key 'N'.
+ N = 78,
+ //! Key 'O'.
+ O = 79,
+ //! Key 'P'.
+ P = 80,
+ //! Key 'Q'.
+ Q = 81,
+ //! Key 'R'.
+ R = 82,
+ //! Key 'S'.
+ S = 83,
+ //! Key 'T'.
+ T = 84,
+ //! Key 'U'.
+ U = 85,
+ //! Key 'V'.
+ V = 86,
+ //! Key 'W'.
+ W = 87,
+ //! Key 'X'.
+ X = 88,
+ //! Key 'Y'.
+ Y = 89,
+ //! Key 'Z'.
+ Z = 90,
+ //! Left bracket ([).
+ LEFT_BRACKET = 91,
+ //! Backslash (\).
+ BACKSLASH = 92,
+ //! Right bracket (]).
+ RIGHT_BRACKET = 93,
+ //! Grave accent (`).
+ GRAVE_ACCENT = 96,
+ //! Non-US key #1.
+ WORLD1 = 161,
+ //! Non-US key #2.
+ WORLD2 = 162,
+ //! Escape key.
+ ESCAPE = 256,
+ //! Enter key.
+ ENTER = 257,
+ //! Tab key.
+ TAB = 258,
+ //! Backspace key.
+ BACKSPACE = 259,
+ //! Insert key.
+ INSERT = 260,
+ //! Delete key.
+ DELETE = 261,
+ //! Right arrow key.
+ RIGHT = 262,
+ //! Left arrow key.
+ LEFT = 263,
+ //! Down arrow key.
+ DOWN = 264,
+ //! Up arrow key.
+ UP = 265,
+ //! Page Up key.
+ PAGE_UP = 266,
+ //! Page Down key.
+ PAGE_DOWN = 267,
+ //! Home key.
+ HOME = 268,
+ //! End key.
+ END = 269,
+ //! Caps Lock key.
+ CAPS_LOCK = 280,
+ //! Scroll Lock key.
+ SCROLL_LOCK = 281,
+ //! Num Lock key.
+ NUM_LOCK = 282,
+ //! Print Screen key.
+ PRINT_SCREEN = 283,
+ //! Pause key.
+ PAUSE = 284,
+ /**
+ * \name Function keys (F1-F25).
+ * \{
+ */
+ F1 = 290,
+ F2 = 291,
+ F3 = 292,
+ F4 = 293,
+ F5 = 294,
+ F6 = 295,
+ F7 = 296,
+ F8 = 297,
+ F9 = 298,
+ F10 = 299,
+ F11 = 300,
+ F12 = 301,
+ F13 = 302,
+ F14 = 303,
+ F15 = 304,
+ F16 = 305,
+ F17 = 306,
+ F18 = 307,
+ F19 = 308,
+ F20 = 309,
+ F21 = 310,
+ F22 = 311,
+ F23 = 312,
+ F24 = 313,
+ F25 = 314,
+ /// \}
+ /**
+ * \name Keypad digits and operators.
+ * \{
+ */
+ KP0 = 320,
+ KP1 = 321,
+ KP2 = 322,
+ KP3 = 323,
+ KP4 = 324,
+ KP5 = 325,
+ KP6 = 326,
+ KP7 = 327,
+ KP8 = 328,
+ KP9 = 329,
+ KP_DECIMAL = 330,
+ KP_DIVIDE = 331,
+ KP_MULTIPLY = 332,
+ KP_SUBTRACT = 333,
+ KP_ADD = 334,
+ KP_ENTER = 335,
+ KP_EQUAL = 336,
+ /// \}
+ /**
+ * \name Modifier keys.
+ * \{
+ */
+ //! Modifier keys.
+ LEFT_SHIFT = 340,
+ LEFT_CONTROL = 341,
+ LEFT_ALT = 342,
+ LEFT_SUPER = 343,
+ RIGHT_SHIFT = 344,
+ RIGHT_CONTROL = 345,
+ RIGHT_ALT = 346,
+ RIGHT_SUPER = 347,
+ /// \}
+ //! Menu key.
+ MENU = 348,
+};
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index d352ea6..b8b2bda 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -71,7 +71,6 @@ SDLContext::~SDLContext() {
IMG_Quit();
SDL_Quit();
}
-
void SDLContext::handle_events(bool & running) {
//TODO: wouter i need events
/*
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 560e2bc..7b4cc43 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -18,6 +18,12 @@ endfunction()
add_example(asset_manager)
add_example(savemgr)
+add_example(proxy)
+add_example(db)
+add_example(ecs)
+add_example(scene_manager)
+add_example(events)
+add_example(particles)
add_example(rendering_particle)
add_example(gameloop)
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 61dd2d9..dc985a3 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -3,6 +3,7 @@ target_sources(test_main PUBLIC
PhysicsTest.cpp
ScriptTest.cpp
ParticleTest.cpp
+ EventTest.cpp
ECSTest.cpp
SceneManagerTest.cpp
ValueBrokerTest.cpp
diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp
new file mode 100644
index 0000000..b0e6c9c
--- /dev/null
+++ b/src/test/EventTest.cpp
@@ -0,0 +1,254 @@
+
+#include "api/Event.h"
+#include "api/EventManager.h"
+#include "api/IKeyListener.h"
+#include "api/IMouseListener.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+using namespace std;
+using namespace std::chrono_literals;
+using namespace crepe;
+
+class EventManagerTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ // Clear any existing subscriptions or events before each test
+ EventManager::get_instance().clear();
+ }
+
+ void TearDown() override {
+ // Ensure cleanup after each test
+ EventManager::get_instance().clear();
+ }
+};
+class MockKeyListener : public IKeyListener {
+public:
+ MOCK_METHOD(bool, on_key_pressed, (const KeyPressEvent & event), (override));
+ MOCK_METHOD(bool, on_key_released, (const KeyReleaseEvent & event), (override));
+};
+
+class MockMouseListener : public IMouseListener {
+public:
+ MOCK_METHOD(bool, on_mouse_clicked, (const MouseClickEvent & event), (override));
+ MOCK_METHOD(bool, on_mouse_pressed, (const MousePressEvent & event), (override));
+ MOCK_METHOD(bool, on_mouse_released, (const MouseReleaseEvent & event), (override));
+ MOCK_METHOD(bool, on_mouse_moved, (const MouseMoveEvent & event), (override));
+};
+
+TEST_F(EventManagerTest, EventSubscription) {
+ EventHandler<KeyPressEvent> key_handler = [](const KeyPressEvent & e) {
+ std::cout << "Key Event Triggered" << std::endl;
+ return true;
+ };
+
+ // Subscribe to KeyPressEvent
+ EventManager::get_instance().subscribe<KeyPressEvent>(key_handler, 1);
+
+ // Verify subscription (not directly verifiable; test by triggering event)
+
+ EventManager::get_instance().trigger_event<KeyPressEvent>(
+ KeyPressEvent{
+ .repeat = true,
+ .key = Keycode::A,
+ },
+ 1);
+ EventManager::get_instance().trigger_event<KeyPressEvent>(
+ KeyPressEvent{
+ .repeat = true,
+ .key = Keycode::A,
+
+ },
+ EventManager::CHANNEL_ALL);
+}
+TEST_F(EventManagerTest, EventManagerTest_trigger_all_channels) {
+ bool triggered = false;
+
+ EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) {
+ triggered = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false;
+ };
+ EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler,
+ EventManager::CHANNEL_ALL);
+
+ MouseClickEvent click_event{
+ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE};
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event,
+ EventManager::CHANNEL_ALL);
+
+ EXPECT_TRUE(triggered);
+}
+TEST_F(EventManagerTest, EventManagerTest_trigger_one_channel) {
+ bool triggered = false;
+ int test_channel = 1;
+ EventHandler<MouseClickEvent> mouse_handler = [&](const MouseClickEvent & e) {
+ triggered = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false;
+ };
+ EventManager::get_instance().subscribe<MouseClickEvent>(mouse_handler, test_channel);
+
+ MouseClickEvent click_event{
+ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE};
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event,
+ EventManager::CHANNEL_ALL);
+
+ EXPECT_FALSE(triggered);
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, test_channel);
+}
+
+TEST_F(EventManagerTest, EventManagerTest_callback_propagation) {
+ EventManager & event_manager = EventManager::get_instance();
+
+ // Flags to track handler calls
+ bool triggered_true = false;
+ bool triggered_false = false;
+
+ // Handlers
+ EventHandler<MouseClickEvent> mouse_handler_true = [&](const MouseClickEvent & e) {
+ triggered_true = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return true; // Stops propagation
+ };
+
+ EventHandler<MouseClickEvent> mouse_handler_false = [&](const MouseClickEvent & e) {
+ triggered_false = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+
+ // Test event
+ MouseClickEvent click_event{
+ .mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE};
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL);
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL);
+
+ // Trigger event
+ event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL);
+
+ // Check that only the true handler was triggered
+ EXPECT_TRUE(triggered_true);
+ EXPECT_FALSE(triggered_false);
+
+ // Reset and clear
+ triggered_true = false;
+ triggered_false = false;
+ event_manager.clear();
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_false, EventManager::CHANNEL_ALL);
+ event_manager.subscribe<MouseClickEvent>(mouse_handler_true, EventManager::CHANNEL_ALL);
+
+ // Trigger event again
+ event_manager.trigger_event<MouseClickEvent>(click_event, EventManager::CHANNEL_ALL);
+
+ // Check that both handlers were triggered
+ EXPECT_TRUE(triggered_true);
+ EXPECT_TRUE(triggered_false);
+}
+
+TEST_F(EventManagerTest, EventManagerTest_queue_dispatch) {
+ EventManager & event_manager = EventManager::get_instance();
+ bool triggered1 = false;
+ bool triggered2 = false;
+ int test_channel = 1;
+ EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) {
+ triggered1 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+ EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) {
+ triggered2 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+ event_manager.subscribe<MouseClickEvent>(mouse_handler1);
+ event_manager.subscribe<MouseClickEvent>(mouse_handler2, test_channel);
+
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE},
+ test_channel);
+ event_manager.dispatch_events();
+ EXPECT_TRUE(triggered1);
+ EXPECT_TRUE(triggered2);
+}
+
+TEST_F(EventManagerTest, EventManagerTest_unsubscribe) {
+ EventManager & event_manager = EventManager::get_instance();
+
+ // Flags to track if handlers are triggered
+ bool triggered1 = false;
+ bool triggered2 = false;
+
+ // Define EventHandlers
+ EventHandler<MouseClickEvent> mouse_handler1 = [&](const MouseClickEvent & e) {
+ triggered1 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+
+ EventHandler<MouseClickEvent> mouse_handler2 = [&](const MouseClickEvent & e) {
+ triggered2 = true;
+ EXPECT_EQ(e.mouse_x, 100);
+ EXPECT_EQ(e.mouse_y, 200);
+ EXPECT_EQ(e.button, MouseButton::LEFT_MOUSE);
+ return false; // Allows propagation
+ };
+ // Subscribe handlers
+ subscription_t handler1_id = event_manager.subscribe<MouseClickEvent>(mouse_handler1);
+ subscription_t handler2_id = event_manager.subscribe<MouseClickEvent>(mouse_handler2);
+
+ // Queue events
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+
+ // Dispatch events - both handlers should be triggered
+ event_manager.dispatch_events();
+ EXPECT_TRUE(triggered1); // Handler 1 should be triggered
+ EXPECT_TRUE(triggered2); // Handler 2 should be triggered
+
+ // Reset flags
+ triggered1 = false;
+ triggered2 = false;
+
+ // Unsubscribe handler1
+ event_manager.unsubscribe(handler1_id);
+
+ // Queue the same event again
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+
+ // Dispatch events - only handler 2 should be triggered, handler 1 should NOT
+ event_manager.dispatch_events();
+ EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered
+ EXPECT_TRUE(triggered2); // Handler 2 should be triggered
+
+ // Reset flags
+ triggered2 = false;
+
+ // Unsubscribe handler2
+ event_manager.unsubscribe(handler2_id);
+
+ // Queue the event again
+ event_manager.queue_event<MouseClickEvent>(
+ MouseClickEvent{.mouse_x = 100, .mouse_y = 200, .button = MouseButton::LEFT_MOUSE});
+
+ // Dispatch events - no handler should be triggered
+ event_manager.dispatch_events();
+ EXPECT_FALSE(triggered1); // Handler 1 should NOT be triggered
+ EXPECT_FALSE(triggered2); // Handler 2 should NOT be triggered
+}