diff --git a/SDL2pp/Private/EventDispatching.hh b/SDL2pp/Private/EventDispatching.hh index 63b01b6..d91a40b 100644 --- a/SDL2pp/Private/EventDispatching.hh +++ b/SDL2pp/Private/EventDispatching.hh @@ -22,386 +22,139 @@ #ifndef SDL2PP_PRIVATE_EVENTDISPATCHING_HH #define SDL2PP_PRIVATE_EVENTDISPATCHING_HH -#include - -#include - -#include -#include +#include namespace SDL2pp { -namespace Private -{ +/* + * This is code not to be used directly by users of SDL2pp. + */ +namespace Private { + /* + * Templated function to dynamically dispatch an event of type EventType to an event handler functor of type EventHandler. + * + * This will be only called if 'eventHandler(event)' + */ template - void DispatchEventToObject(const EventType &event, EventHandler&& eventHandler) { - eventHandler.HandleEvent(event); - } - - template - void DispatchEventToFunctor(const EventType &event, EventHandler&& eventHandler) { + auto DispatchEventHandlerFunctor(const EventType &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type + { eventHandler(event); } - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type + /* + * Templated function to do nothing when trying to dispatch an event of type EventType to an invalid event handler functor of type EventHandler. + */ + template + auto DispatchEventHandlerFunctor(const EventType &, EventHandler&&) -> typename std::enable_if::value>::type { - DispatchEventToObject(event, eventHandler); + // no-op } - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type + /* + * Templated function to do nothing when trying to dispatch an event of type EventType to an invalid event handler object of type EventHandler. + */ + template + auto DispatchEventHandlerObject(const EventType &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type { - DispatchEventToFunctor(event, eventHandler); + eventHandler.HandleEvent(event); } -#if SDL_VERSION_ATLEAST(2, 0, 5) - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type + /* + * Templated function to dynamically dispatch an event of type EventType to an event handler object of type EventHandler. + * + * This will be only called if 'eventHandler.HandleEvent(event)' + */ + template + auto DispatchEventHandlerObject(const EventType &, EventHandler&&) -> typename std::enable_if::value>::type { - if (event.type == SDL_AUDIODEVICEADDED || event.type == SDL_AUDIODEVICEREMOVED) - DispatchEventToObject(event.type, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_AUDIODEVICEADDED || event.type == SDL_AUDIODEVICEREMOVED) - DispatchEventToFunctor(event.type, eventHandler); - } -#endif - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_CONTROLLERAXISMOTION) - DispatchEventToObject(event.caxis, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_CONTROLLERAXISMOTION) - DispatchEventToFunctor(event.caxis, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONDOWN) - DispatchEventToObject(event.cbutton, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONDOWN) - DispatchEventToFunctor(event.cbutton, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_CONTROLLERDEVICEADDED || event.type == SDL_CONTROLLERDEVICEREMOVED || event.type == SDL_CONTROLLERDEVICEREMAPPED) - DispatchEventToObject(event.cdevice, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_CONTROLLERDEVICEADDED || event.type == SDL_CONTROLLERDEVICEREMOVED || event.type == SDL_CONTROLLERDEVICEREMAPPED) - DispatchEventToFunctor(event.cdevice, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_DOLLARGESTURE || event.type == SDL_DOLLARRECORD) - DispatchEventToObject(event.dgesture, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_DOLLARGESTURE || event.type == SDL_DOLLARRECORD) - DispatchEventToFunctor(event.dgesture, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_DROPFILE || event.type == SDL_DROPTEXT -#if SDL_VERSION_ATLEAST(2, 0, 5) - || event.type == SDL_DROPBEGIN || event.type == SDL_DROPCOMPLETE -#endif - ) - DispatchEventToObject(event.drop, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_DROPFILE || event.type == SDL_DROPTEXT -#if SDL_VERSION_ATLEAST(2, 0, 5) - || event.type == SDL_DROPBEGIN || event.type == SDL_DROPCOMPLETE -#endif - ) - DispatchEventToObject(event.drop, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYAXISMOTION) - DispatchEventToObject(event.jaxis, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYAXISMOTION) - DispatchEventToFunctor(event.jaxis, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYBALLMOTION) - DispatchEventToObject(event.jball, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYBALLMOTION) - DispatchEventToFunctor(event.jball, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP) - DispatchEventToObject(event.jbutton, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP) - DispatchEventToFunctor(event.jbutton, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYDEVICEADDED || event.type == SDL_JOYDEVICEREMOVED) - DispatchEventToObject(event.jdevice, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYDEVICEADDED || event.type == SDL_JOYDEVICEREMOVED) - DispatchEventToFunctor(event.jdevice, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYHATMOTION) - DispatchEventToObject(event.jhat, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_JOYHATMOTION) - DispatchEventToFunctor(event.jhat, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) - DispatchEventToObject(event.key, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) - DispatchEventToFunctor(event.key, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) - DispatchEventToObject(event.button, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) - DispatchEventToFunctor(event.button, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MOUSEMOTION) - DispatchEventToObject(event.motion, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MOUSEMOTION) - DispatchEventToFunctor(event.motion, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MOUSEWHEEL) - DispatchEventToObject(event.wheel, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MOUSEWHEEL) - DispatchEventToFunctor(event.wheel, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MULTIGESTURE) - DispatchEventToObject(event.mgesture, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_MULTIGESTURE) - DispatchEventToFunctor(event.mgesture, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_QUIT) - DispatchEventToObject(event.quit, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_QUIT) - DispatchEventToFunctor(event.quit, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_SYSWMEVENT) - DispatchEventToObject(event.syswm, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_SYSWMEVENT) - DispatchEventToFunctor(event.syswm, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_TEXTEDITING) - DispatchEventToObject(event.edit, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_TEXTEDITING) - DispatchEventToFunctor(event.edit, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_TEXTINPUT) - DispatchEventToObject(event.text, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_TEXTINPUT) - DispatchEventToFunctor(event.text, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_FINGERMOTION || event.type == SDL_FINGERDOWN || event.type == SDL_FINGERUP) - DispatchEventToObject(event.tfinger, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_FINGERMOTION || event.type == SDL_FINGERDOWN || event.type == SDL_FINGERUP) - DispatchEventToFunctor(event.tfinger, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_USEREVENT) - DispatchEventToObject(event.user, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_USEREVENT) - DispatchEventToFunctor(event.user, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_WINDOWEVENT) - DispatchEventToObject(event.window, eventHandler); - } - - template - auto DispatchSpecificEvent(const SDL_Event &event, EventHandler&& eventHandler) -> typename std::enable_if::value>::type - { - if (event.type == SDL_WINDOWEVENT) - DispatchEventToFunctor(event.window, eventHandler); + // no-op } - // TODO: Find an easy way to make IsEventHandler - /*template - auto DispatchSpecificEvent(const SDL_Event &, EventHandler&&) -> typename std::enable_if::value>::type - { - static_assert(IsEventHandler::value, "Event handler is not a valid functor or object"); - }*/ + /* + * Templated class to dispatch an event to a given event handler: + * + * EventHandler is the type of the event handler + * ValidEventHandler is a boolean to detect if the event was actially valid for any of the event types + * EvenTypes is a tuple containing a list of valid event types + * + * Basically, this class would be roughly the equivalent of the following pseudocode: + * + * DispatchEvent(SDL_Event event, EventHandler eventHandler, EventTypes eventTypes) { + * auto validEventHandler = false; + * + * for (auto eventType : eventTypes) { + * validEventHandler |= IsValidEventHandler(EventHandler, eventType); + * if (ShouldHandleEvent(event)) { + * DispatchEventToFunctor(event, eventHandler); + * DispatchEventToObject(event, eventHandler); + * } + * } + * + * if (!validEventHandler) throw; + * } + */ + template + struct EventDispatcher; + + /* + * Instatiation of the class to dispatch an event to a given event handler, expansion to a given type: + * + * The head of the tuple is "peeled" and used to dispatch the event to the event handler. + * The tail then is passed to another expansion of EventDispatcher along with the calculated value of IsEventHandler + * + */ + template + struct EventDispatcher> { + static constexpr bool IsValidEventHandler = ValidEventHandler || IsEventHandler::value; + + using Filter = EventTypeFilter; + + static void DispatchEvent(const SDL_Event &event, EventHandler&& eventHandler) { + if (Filter::ShouldHandleEvent(event)) { + DispatchEventHandlerFunctor(Filter::GetEventByType(event), std::forward(eventHandler)); + DispatchEventHandlerObject(Filter::GetEventByType(event), std::forward(eventHandler)); + + } + + EventDispatcher>::DispatchEvent(event, std::forward(eventHandler)); + } + }; + + /* + * Instantiation of the class to dispatch an event to a given event handler, final expansion: + * + * The tuple of valid event types is empty and the value of the ValidEventHandler boolean + * is placed in the IsValidEventHandler variable, finally when an event gets dispatched + * an static_assert happens to verify if the event handler actually handled any events. + */ + template + struct EventDispatcher> { + static constexpr auto IsValidEventHandler = ValidEventHandler; + + static void DispatchEvent(const SDL_Event &, EventHandler&&) { + static_assert(IsValidEventHandler, "One of the given event handlers is not a valid one"); + } + }; + /* + * Templated class expand a list of event handlers so they can be dispatched. + */ template void DispatchEvent(const SDL_Event &, EventHandlers&&...); - + + /* + * Instantiation of the class to expand a list of event handlers so they can be dispatched. + * + * This "peels" the first event handler from the arguments, dispatchs it and then expands the tail of the list. + */ template void DispatchEvent(const SDL_Event &event, EventHandler&& eventHandler, EventHandlers&&... eventHandlers) { - DispatchSpecificEvent(event, eventHandler); + EventDispatcher::DispatchEvent(event, std::forward(eventHandler)); DispatchEvent(event, eventHandlers...); } - + + /* + * Instantiation of the class to expand a list of event handlers, when the list is empty. + */ template <> void DispatchEvent(const SDL_Event &) { // no-op diff --git a/tests/test_eventdispatching.cc b/tests/test_eventdispatching.cc index 183505f..903d526 100644 --- a/tests/test_eventdispatching.cc +++ b/tests/test_eventdispatching.cc @@ -6,64 +6,127 @@ using namespace SDL2pp::Private; -static auto globalResult = Sint32{0}; +namespace TestFreeFunctions { + static auto globalResult = Sint32{0}; -inline void handler(SDL_Event event) -{ - globalResult = event.user.code; + inline void handler(SDL_Event event) { + globalResult = event.user.code; + } } BEGIN_TEST(int, char*[]) + // These test require a major rework, commenting everything in the mean time + SDL_Event event; + event.type = SDL_QUIT; + event.user.code = 31; -SDL_Event event; -event.type = SDL_USEREVENT; -event.user.code = 31; + // Test dispatching with no event handler + DispatchEvent(event); -DispatchEvent(event); - -auto result = Sint32{0}; -DispatchEvent(event, [&result](SDL_Event event) { - result = event.user.code; -}); -EXPECT_EQUAL(event.user.code, result); - -DispatchEvent(event, handler); -EXPECT_EQUAL(event.user.code, globalResult); - -struct EventHandlerFunctor { - Sint32 result; - - void operator()(SDL_Event event) { - result = event.user.code; + // Test dispatching with lambda as event handler + { + auto result = Sint32{0}; + DispatchEvent(event, [&result](SDL_Event event) { + result = event.user.code; + }); + EXPECT_EQUAL(event.user.code, result); } -}; -auto eventHandlerFunctor = EventHandlerFunctor{}; -DispatchEvent(event, eventHandlerFunctor); -EXPECT_EQUAL(event.user.code, eventHandlerFunctor.result); - -struct EventHandlerObject { - Sint32 result; - - void HandleEvent(SDL_Event event) { - result = event.user.code; + // Test dispatching with function as event handler + { + DispatchEvent(event, TestFreeFunctions::handler); + EXPECT_EQUAL(event.user.code, TestFreeFunctions::globalResult); } -}; -auto eventHandlerObject = EventHandlerObject{}; -DispatchEvent(event, eventHandlerObject); -EXPECT_EQUAL(event.user.code, eventHandlerObject.result); + // Test dispatching with a functor as event handler + { + struct EventHandlerFunctor { + Sint32 result; -struct SpecificEventHandler { - Sint32 result; + void operator()(SDL_Event event) { + result = event.user.code; + } + }; + + auto eventHandlerFunctor = EventHandlerFunctor{}; + DispatchEvent(event, eventHandlerFunctor); + EXPECT_EQUAL(event.user.code, eventHandlerFunctor.result); + } + + // Test dispatching with an object as event handler + { + struct EventHandlerObject { + Sint32 result; + + void HandleEvent(SDL_Event event) { + result = event.user.code; + } + }; + + auto eventHandlerObject = EventHandlerObject{}; + DispatchEvent(event, eventHandlerObject); + EXPECT_EQUAL(event.user.code, eventHandlerObject.result); + } + + // Test several event handlers + { + struct EventHandlerFunctor { + bool executed = false; + + void operator()(SDL_QuitEvent) { + executed = true; + } + }; + auto eventHandlerFunctor1 = EventHandlerFunctor{}; + auto eventHandlerFunctor2 = EventHandlerFunctor{}; + + struct EventHandlerObject { + bool executed = false; + + void HandleEvent(SDL_QuitEvent) { + executed = true; + } + }; + auto eventHandlerObject = EventHandlerObject{}; - void HandleEvent(SDL_UserEvent event) { - result = event.code; + auto lambdaExecuted = false; + auto lambda = [&lambdaExecuted](SDL_QuitEvent) { lambdaExecuted = true; }; + + DispatchEvent(event, eventHandlerFunctor1, eventHandlerFunctor2, eventHandlerObject, lambda); + EXPECT_TRUE(eventHandlerFunctor1.executed); + EXPECT_TRUE(eventHandlerFunctor2.executed); + EXPECT_TRUE(eventHandlerObject.executed); + EXPECT_TRUE(lambdaExecuted); } -}; - -auto specificEventHandler = SpecificEventHandler{}; -DispatchEvent(event, specificEventHandler); -EXPECT_EQUAL(event.user.code, specificEventHandler.result); + // Test event handler with several event types + { + struct EventHandler { + bool quitEventExecuted = false; + bool keyboardEventExecuted = false; + + void HandleEvent(SDL_QuitEvent) { + quitEventExecuted = true; + } + + void HandleEvent(SDL_KeyboardEvent) { + keyboardEventExecuted = true; + } + }; + + auto eventHandler = EventHandler{}; + DispatchEvent(event, eventHandler); + + EXPECT_TRUE(eventHandler.quitEventExecuted); + EXPECT_TRUE(!eventHandler.keyboardEventExecuted); + + eventHandler.quitEventExecuted = false; + + event.type = SDL_KEYUP; + + DispatchEvent(event, eventHandler); + + EXPECT_TRUE(!eventHandler.quitEventExecuted); + EXPECT_TRUE(eventHandler.keyboardEventExecuted); + } END_TEST()