aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-11-21 09:30:51 +0100
committerLoek Le Blansch <loek@pipeframe.xyz>2024-11-21 09:30:51 +0100
commite2f085c444a8b37af65816e10bf366e6860d25c2 (patch)
treecec8c3e5640f659478838534ad64b86502bc8e29 /src
parent9d66b6cfccd15a1600c30af5e744e8b0710eeae6 (diff)
parent1cc120a0031cfc19c35240da8390d9129b4d75a3 (diff)
merge `master` into `loek/util`
Diffstat (limited to 'src')
-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.cpp46
-rw-r--r--src/crepe/api/EventManager.h161
-rw-r--r--src/crepe/api/EventManager.hpp36
-rw-r--r--src/crepe/api/IKeyListener.cpp19
-rw-r--r--src/crepe/api/IKeyListener.h49
-rw-r--r--src/crepe/api/IMouseListener.cpp29
-rw-r--r--src/crepe/api/IMouseListener.h72
-rw-r--r--src/crepe/api/KeyCodes.h153
-rw-r--r--src/crepe/facade/SDLContext.cpp4
-rw-r--r--src/test/CMakeLists.txt1
-rw-r--r--src/test/DBTest.cpp3
-rw-r--r--src/test/EventTest.cpp254
-rw-r--r--src/test/ValueBrokerTest.cpp5
18 files changed, 1067 insertions, 7 deletions
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 6557656..d6b6801 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -17,9 +17,13 @@ target_sources(crepe PUBLIC
Vector2.cpp
Camera.cpp
Animator.cpp
+ EventManager.cpp
+ IKeyListener.cpp
+ IMouseListener.cpp
LoopManager.cpp
LoopTimer.cpp
Asset.cpp
+ EventHandler.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -44,6 +48,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
Asset.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..20f0dd3
--- /dev/null
+++ b/src/crepe/api/EventManager.cpp
@@ -0,0 +1,46 @@
+#include "EventManager.h"
+
+using namespace crepe;
+using namespace std;
+
+EventManager & EventManager::get_instance() {
+ static EventManager instance;
+ return instance;
+}
+
+void EventManager::dispatch_events() {
+ for (auto & event : this->events_queue) {
+ this->handle_event(event.type, event.channel, *event.event.get());
+ }
+ this->events_queue.clear();
+}
+
+void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) {
+ auto handlers_it = this->subscribers.find(type);
+ if (handlers_it == this->subscribers.end()) return;
+
+ vector<CallbackEntry> & handlers = handlers_it->second;
+ for (auto & handler : handlers) {
+ bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL;
+ if (check_channel && handler.channel != channel) continue;
+
+ bool handled = handler.callback->exec(data);
+ if (handled) return;
+ }
+}
+
+void EventManager::clear() {
+ this->subscribers.clear();
+ this->events_queue.clear();
+}
+
+void EventManager::unsubscribe(subscription_t id) {
+ for (auto & [event_type, handlers] : this->subscribers) {
+ for (auto it = handlers.begin(); it != handlers.end(); it++) {
+ // find listener with subscription id
+ if ((*it).id != id) continue;
+ it = handlers.erase(it);
+ return;
+ }
+ }
+}
diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h
new file mode 100644
index 0000000..348a04d
--- /dev/null
+++ b/src/crepe/api/EventManager.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#include "Event.h"
+#include "EventHandler.h"
+
+namespace crepe {
+
+//! Event listener unique ID
+typedef size_t subscription_t;
+
+/**
+ * \brief Event channel
+ *
+ * Events can be sent to a specific channel, which prevents listeners on other channels from
+ * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners.
+ */
+typedef size_t event_channel_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 const event_channel_t 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,
+ event_channel_t 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, event_channel_t 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, event_channel_t 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.
+ event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event.
+ std::type_index type; ///< The type of the event.
+ };
+
+ /**
+ * \brief Internal event handler
+ *
+ * This function processes a single event, and is used to process events both during
+ * EventManager::dispatch_events and inside EventManager::trigger_event
+ *
+ * \param type \c typeid of concrete Event class
+ * \param channel Event channel
+ * \param data Event data
+ */
+ void handle_event(std::type_index type, event_channel_t channel, const Event & data);
+
+ /**
+ * \struct CallbackEntry
+ * \brief Represents a registered event handler callback.
+ */
+ struct CallbackEntry {
+ std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper.
+ event_channel_t 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..a5f4556
--- /dev/null
+++ b/src/crepe/api/EventManager.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "EventManager.h"
+
+namespace crepe {
+
+template <typename EventType>
+subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
+ event_channel_t 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, event_channel_t channel) {
+ static_assert(std::is_base_of<Event, EventType>::value,
+ "EventType must derive from Event");
+ this->events_queue.push_back(QueueEntry{
+ .event = std::make_unique<EventType>(event),
+ .channel = channel,
+ .type = typeid(EventType),
+ });
+}
+
+template <typename EventType>
+void EventManager::trigger_event(const EventType & event, event_channel_t channel) {
+ this->handle_event(typeid(EventType), channel, event);
+}
+
+} // namespace crepe
diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp
new file mode 100644
index 0000000..8642655
--- /dev/null
+++ b/src/crepe/api/IKeyListener.cpp
@@ -0,0 +1,19 @@
+#include "IKeyListener.h"
+
+using namespace crepe;
+
+// Constructor with specified channel
+IKeyListener::IKeyListener(event_channel_t 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..328a4c2
--- /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(event_channel_t 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..989aeb3
--- /dev/null
+++ b/src/crepe/api/IMouseListener.cpp
@@ -0,0 +1,29 @@
+#include "IMouseListener.h"
+
+using namespace crepe;
+
+IMouseListener::IMouseListener(event_channel_t 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..15e1619
--- /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(event_channel_t 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..9e173e0
--- /dev/null
+++ b/src/crepe/api/KeyCodes.h
@@ -0,0 +1,153 @@
+#pragma once
+
+//! Enumeration for mouse button inputs, including standard and extended buttons.
+enum class MouseButton {
+ NONE = 0, //!< No mouse button input.
+ LEFT_MOUSE = 1, //!< Left mouse button.
+ RIGHT_MOUSE = 2, //!< Right mouse button.
+ MIDDLE_MOUSE = 3, //!< Middle mouse button (scroll wheel press).
+ X1_MOUSE = 4, //!< First extended mouse button.
+ X2_MOUSE = 5, //!< Second extended mouse button.
+ SCROLL_UP = 6, //!< Scroll wheel upward movement.
+ SCROLL_DOWN = 7, //!< Scroll wheel downward movement.
+};
+
+//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys.
+enum class Keycode {
+ NONE = 0, //!< No key input.
+ SPACE = 32, //!< Spacebar.
+ APOSTROPHE = 39, //!< Apostrophe (').
+ COMMA = 44, //!< Comma (,).
+ MINUS = 45, //!< Minus (-).
+ PERIOD = 46, //!< Period (.).
+ SLASH = 47, //!< Slash (/).
+ D0 = 48, //!< Digit 0.
+ D1 = 49, //!< Digit 1.
+ D2 = 50, //!< Digit 2.
+ D3 = 51, //!< Digit 3.
+ D4 = 52, //!< Digit 4.
+ D5 = 53, //!< Digit 5.
+ D6 = 54, //!< Digit 6.
+ D7 = 55, //!< Digit 7.
+ D8 = 56, //!< Digit 8.
+ D9 = 57, //!< Digit 9.
+ SEMICOLON = 59, //!< Semicolon (;).
+ EQUAL = 61, //!< Equal sign (=).
+ A = 65, //!< Key 'A'.
+ B = 66, //!< Key 'B'.
+ C = 67, //!< Key 'C'.
+ D = 68, //!< Key 'D'.
+ E = 69, //!< Key 'E'.
+ F = 70, //!< Key 'F'.
+ G = 71, //!< Key 'G'.
+ H = 72, //!< Key 'H'.
+ I = 73, //!< Key 'I'.
+ J = 74, //!< Key 'J'.
+ K = 75, //!< Key 'K'.
+ L = 76, //!< Key 'L'.
+ M = 77, //!< Key 'M'.
+ N = 78, //!< Key 'N'.
+ O = 79, //!< Key 'O'.
+ P = 80, //!< Key 'P'.
+ Q = 81, //!< Key 'Q'.
+ R = 82, //!< Key 'R'.
+ S = 83, //!< Key 'S'.
+ T = 84, //!< Key 'T'.
+ U = 85, //!< Key 'U'.
+ V = 86, //!< Key 'V'.
+ W = 87, //!< Key 'W'.
+ X = 88, //!< Key 'X'.
+ Y = 89, //!< Key 'Y'.
+ Z = 90, //!< Key 'Z'.
+ LEFT_BRACKET = 91, //!< Left bracket ([).
+ BACKSLASH = 92, //!< Backslash (\).
+ RIGHT_BRACKET = 93, //!< Right bracket (]).
+ GRAVE_ACCENT = 96, //!< Grave accent (`).
+ WORLD1 = 161, //!< Non-US key #1.
+ WORLD2 = 162, //!< Non-US key #2.
+ ESCAPE = 256, //!< Escape key.
+ ENTER = 257, //!< Enter key.
+ TAB = 258, //!< Tab key.
+ BACKSPACE = 259, //!< Backspace key.
+ INSERT = 260, //!< Insert key.
+ DELETE = 261, //!< Delete key.
+ RIGHT = 262, //!< Right arrow key.
+ LEFT = 263, //!< Left arrow key.
+ DOWN = 264, //!< Down arrow key.
+ UP = 265, //!< Up arrow key.
+ PAGE_UP = 266, //!< Page Up key.
+ PAGE_DOWN = 267, //!< Page Down key.
+ HOME = 268, //!< Home key.
+ END = 269, //!< End key.
+ CAPS_LOCK = 280, //!< Caps Lock key.
+ SCROLL_LOCK = 281, //!< Scroll Lock key.
+ NUM_LOCK = 282, //!< Num Lock key.
+ PRINT_SCREEN = 283, //!< Print Screen key.
+ PAUSE = 284, //!< Pause key.
+ /**
+ * \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.
+ * \{
+ */
+ 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 = 348, //!< Menu key.
+};
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index d352ea6..00523a6 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -47,7 +47,8 @@ SDLContext::SDLContext() {
SDL_Renderer * tmp_renderer
= SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
if (!tmp_renderer) {
- throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
+ throw runtime_error(
+ format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
}
this->game_renderer
@@ -71,7 +72,6 @@ SDLContext::~SDLContext() {
IMG_Quit();
SDL_Quit();
}
-
void SDLContext::handle_events(bool & running) {
//TODO: wouter i need events
/*
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 7310bb8..8cb4232 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -6,6 +6,7 @@ target_sources(test_main PUBLIC
AssetTest.cpp
OptionalRefTest.cpp
RenderSystemTest.cpp
+ EventTest.cpp
ECSTest.cpp
SceneManagerTest.cpp
ValueBrokerTest.cpp
diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp
index b57eba9..e80814c 100644
--- a/src/test/DBTest.cpp
+++ b/src/test/DBTest.cpp
@@ -1,5 +1,5 @@
-#include <gtest/gtest.h>
#include <crepe/facade/DB.h>
+#include <gtest/gtest.h>
using namespace std;
using namespace crepe;
@@ -26,4 +26,3 @@ TEST_F(DBTest, Has) {
db.set("foo", "bar");
EXPECT_EQ(db.has("foo"), true);
}
-
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
+}
diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp
index 10a4654..e6bb058 100644
--- a/src/test/ValueBrokerTest.cpp
+++ b/src/test/ValueBrokerTest.cpp
@@ -13,7 +13,7 @@ public:
int write_count = 0;
int value = 0;
- ValueBroker<int> broker {
+ ValueBroker<int> broker{
[this](const int & target) -> void {
this->write_count++;
this->value = target;
@@ -49,7 +49,7 @@ TEST_F(ValueBrokerTest, ProxyWrite) {
EXPECT_EQ(write_count, 1);
}
-void dummy(int) { }
+void dummy(int) {}
TEST_F(ValueBrokerTest, ProxyRead) {
dummy(proxy);
EXPECT_EQ(read_count, 1);
@@ -61,4 +61,3 @@ TEST_F(ValueBrokerTest, ProxyReadWrite) {
ASSERT_EQ(read_count, 1);
ASSERT_EQ(write_count, 1);
}
-