diff --git a/CMakeLists.txt b/CMakeLists.txt index 902c77c..05a3f73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ set(LIBRARY_HEADERS SDL2pp/AudioSpec.hh SDL2pp/Color.hh SDL2pp/ContainerRWops.hh + SDL2pp/EventPolling.hh SDL2pp/Exception.hh SDL2pp/Optional.hh SDL2pp/Point.hh @@ -119,6 +120,10 @@ set(LIBRARY_HEADERS SDL2pp/Texture.hh SDL2pp/Wav.hh SDL2pp/Window.hh + SDL2pp/Private/EventDispatching.hh + SDL2pp/Private/EventHandler.hh + SDL2pp/Private/EventTypeFilters.hh + SDL2pp/Private/Utility.hh ) # optional sources diff --git a/Doxyfile.in b/Doxyfile.in index c8537bd..2a4fc72 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -21,7 +21,8 @@ INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/SDL2pp" \ RECURSIVE = YES # Exclude foreign files -EXCLUDE = "@CMAKE_CURRENT_SOURCE_DIR@/SDL2pp/external" +EXCLUDE = "@CMAKE_CURRENT_SOURCE_DIR@/SDL2pp/external" \ + "@CMAKE_CURRENT_SOURCE_DIR@/SDL2pp/Private" # Examples (doesn't work atm) EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/examples" diff --git a/SDL2pp/EventPolling.hh b/SDL2pp/EventPolling.hh new file mode 100644 index 0000000..6e4e96a --- /dev/null +++ b/SDL2pp/EventPolling.hh @@ -0,0 +1,102 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2017 Vraiment + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL2PP_EVENTPOLLING_HH +#define SDL2PP_EVENTPOLLING_HH + +#include + +namespace SDL2pp { +namespace Event { + //////////////////////////////////////////////////////////// + /// \brief Polls and handles a single event + /// + /// This function tries to poll a single event from the event + /// queue using SDL_PollEvent(). If an event was polled the + /// event handler is called using the retrieved SDL_Event as an + /// argument then this function returns true. If no event was + /// retrieved the event handler is not called and this function + /// returns false. + /// + /// This function accepts the following as event handlers: + /// - Functors (lambdas, free functions, callable objects) + /// - Objects (Objects that have a HandleEvent(EventType) function) + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventPolling.hh + /// + /// \param[in] eventHandlers A list of event handlers that will handle the event + /// + /// \returns True if an event was polled, false otherwise + /// + /// \see https://wiki.libsdl.org/SDL_PollEvent + /// \see https://wiki.libsdl.org/CategoryEvents#Structures + /// + //////////////////////////////////////////////////////////// + template + bool PollEvent(EventHandlers&&... eventHandlers) { + SDL_Event event; + if (!SDL_PollEvent(&event)) { + return false; + } + + Private::DispatchEvent(event, eventHandlers...); + + return true; + } + + //////////////////////////////////////////////////////////// + /// \brief Polls and handles all the events from the event queue + /// + /// This function calls SDL_PollEvent() until the event queue is empty. + /// Then for each event that was polled the event handler is called + /// using the polled event as an argument. This function returns the + /// amount of events that were polled. + /// + /// This function accepts the following as event handlers: + /// - Functors (lambdas, free functions, callable objects) + /// - Objects (Objects that have a HandleEvent(EventType) function) + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventPolling.hh + /// + /// \param[in] eventHandlers A list of event handlers that will handle the polled event + /// + /// \returns The amount of polled events (can be zero) + /// + /// \see https://wiki.libsdl.org/SDL_PollEvent + /// \see https://wiki.libsdl.org/CategoryEvents#Structures + /// + //////////////////////////////////////////////////////////// + template + int PollAllEvents(EventHandlers&&... eventHandlers) { + int result; + + for (result = 0; PollEvent(eventHandlers...); result++); + + return result; + } +} +} + +#endif diff --git a/SDL2pp/Private/EventDispatching.hh b/SDL2pp/Private/EventDispatching.hh new file mode 100644 index 0000000..9108e2c --- /dev/null +++ b/SDL2pp/Private/EventDispatching.hh @@ -0,0 +1,166 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2017 Vraiment + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL2PP_PRIVATE_EVENTDISPATCHING_HH +#define SDL2PP_PRIVATE_EVENTDISPATCHING_HH + +#include + +namespace SDL2pp { +/* + * 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 EventHandlerType. + * + * This will be only called if 'eventHandler(event)' can be called + */ + template + auto DispatchEventHandlerFunctor(const EventType &event, EventHandlerType&& eventHandler) -> typename std::enable_if::value>::type + { + eventHandler(event); + } + + /* + * Templated function to do nothing when trying to dispatch an event of type EventType to an invalid event handler functor of type EventHandlerType. + */ + template + auto DispatchEventHandlerFunctor(const EventType &, EventHandlerType&&) -> typename std::enable_if::value>::type + { + // no-op + } + + /* + * Templated function to dynamically dispatch an event of type EventType to an event handler object of type EventHandlerType. + * + * This will be only called if 'eventHandler.HandleEvent(event)' can be called + */ + template + auto DispatchEventHandlerObject(const EventType &event, EventHandlerType&& eventHandler) -> typename std::enable_if::value>::type + { + eventHandler.HandleEvent(event); + } + + + /* + * Templated function to do nothing when trying to dispatch an event of type EventType to an invalid event handler object of type EventHandlerType. + */ + template + auto DispatchEventHandlerObject(const EventType &, EventHandlerType&&) -> typename std::enable_if::value>::type + { + // no-op + } + + /* + * Templated class to dispatch an event to a given event handler: + * + * ValidEventHandler is a boolean to detect if the event was actially valid for any of the event types + * EventHandlerType is the type of the event handler + * 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, EventHandlerType 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, EventHandlerType&& 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 &, EventHandlerType&&) { + 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 &, EventHandlerTypes&&...); + + /* + * 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, EventHandlerType&& eventHandler, EventHandlerTypes&&... eventHandlers) { + 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 + } +} +} + +#endif diff --git a/SDL2pp/Private/EventHandler.hh b/SDL2pp/Private/EventHandler.hh new file mode 100644 index 0000000..be2c31f --- /dev/null +++ b/SDL2pp/Private/EventHandler.hh @@ -0,0 +1,153 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2017 Vraiment + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL2PP_PRIVATE_EVENTHANDLER_HH +#define SDL2PP_PRIVATE_EVENTHANDLER_HH + +#include + +#include +#include +#include + +#include +#include + +namespace SDL2pp { +/* + * This is code not to be used directly by users of SDL2pp. + */ +namespace Private { + /* + * Tuple containing all the valid events that can be handled. + */ + using ValidEventTypes = std::tuple< + SDL_Event, +#if SDL_VERSION_ATLEAST(2, 0, 5) + SDL_AudioDeviceEvent, +#endif + SDL_ControllerAxisEvent, + SDL_ControllerButtonEvent, + SDL_ControllerDeviceEvent, + SDL_DollarGestureEvent, + SDL_DropEvent, + SDL_JoyAxisEvent, + SDL_JoyBallEvent, + SDL_JoyButtonEvent, + SDL_JoyDeviceEvent, + SDL_JoyHatEvent, + SDL_KeyboardEvent, + SDL_MouseButtonEvent, + SDL_MouseMotionEvent, + SDL_MouseWheelEvent, + SDL_MultiGestureEvent, + SDL_QuitEvent, + SDL_SysWMEvent, + SDL_TextEditingEvent, + SDL_TextInputEvent, + SDL_TouchFingerEvent, + SDL_UserEvent, + SDL_WindowEvent + >; + + /* + * Templated class to identify a class that is not an event handler functor. + */ + template + struct IsEventHandlerFunctor : std::false_type { }; + + /* + * Templated class to identify a class that is an event handler functor, the + * way this is done is by verifying if the functor is assignable to the + * expected signature. + */ + template + struct IsEventHandlerFunctor< + EventHandlerType, + EventType, + typename std::enable_if< + std::is_convertible>::value + >::type + > : std::true_type { }; + + /* + * Templated class to identify a class that is not an event handler object. + */ + template + struct IsEventHandlerObject : std::false_type { }; + + /* + * Templated class to identify a class that is an event handler object, the + * way this is done is by verifying that an instance of EventHandlerType has + * the "HandleEvent" member function which received a "EventType" and + * returns void. + */ + template + struct IsEventHandlerObject< + EventHandlerType, + EventType, + typename std::enable_if< + std::is_same< + decltype(std::declval().HandleEvent(std::declval())), + void + >::value + >::type + > : std::true_type { }; + + /* + * Templated class to identify a class that is not an event handler. + */ + template + struct IsEventHandler : std::false_type { }; + + /* + * Templated class to identify a type that is an event handler, the type + * EventHandlerType is considered to be an event handler if is either an + * event handler object or an event handler functor for the given event + * type. Also the event type must be a valid SDL event type. + * + * In other words, any of the following code can be executed: + * { + * EventType event; + * EventHandlerType eventHandler; + * eventHandler(event); + * // or + * eventHandler.HandleEvent(event); + * } + */ + template + struct IsEventHandler< + EventHandlerType, + EventType, + typename std::enable_if< + And< + Or< + IsEventHandlerObject, + IsEventHandlerFunctor + >, + TupleHasType + >::value + >::type + > : std::true_type { }; +} +} + +#endif diff --git a/SDL2pp/Private/EventTypeFilters.hh b/SDL2pp/Private/EventTypeFilters.hh new file mode 100644 index 0000000..5d7a008 --- /dev/null +++ b/SDL2pp/Private/EventTypeFilters.hh @@ -0,0 +1,107 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2017 Vraiment + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL2PP_PRIVATE_EVENTHANDLERBYTYPE_HH +#define SDL2PP_PRIVATE_EVENTHANDLERBYTYPE_HH + +#include + +#include + +namespace SDL2pp { +/* + * This is code not to be used directly by users of SDL2pp. + */ +namespace Private { + /* + * Templated struct that is used to: + * - Verify an event is of the given type + * - Get the specific event for that given type + */ + template + struct EventTypeFilter { + // Should return true if the given event is of the type EventType + static bool ShouldHandleEvent(const SDL_Event &); + + // Should return the member of SDL_Event that is of the given EventType + static const EventType &GetEventByType(const SDL_Event &event); + }; + + // SDL_Event is defined without a macro because the arguments are not used + // this shutdowns "unnused variables" warnings on some compilers + template <> + struct EventTypeFilter { + static bool ShouldHandleEvent(const SDL_Event &) { + return true; + } + + static const SDL_Event &GetEventByType(const SDL_Event &event) { + return event; + } + }; + +// I use a macro here to make things less verbose +#define SDL2PP_DEFINE_EVENT_MAPPING(eventType, condition, eventObject) template<>\ + struct EventTypeFilter {\ + static bool ShouldHandleEvent(const SDL_Event &event) {\ + return condition;\ + }\ + \ + static const eventType &GetEventByType(const SDL_Event &event) {\ + return eventObject;\ + }\ + } + +#if SDL_VERSION_ATLEAST(2, 0, 4) + SDL2PP_DEFINE_EVENT_MAPPING(SDL_AudioDeviceEvent, event.type == SDL_AUDIODEVICEADDED || event.type == SDL_AUDIODEVICEREMOVED, event.adevice); +#endif + SDL2PP_DEFINE_EVENT_MAPPING(SDL_ControllerAxisEvent, event.type == SDL_CONTROLLERAXISMOTION, event.caxis); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_ControllerButtonEvent, event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP, event.cbutton); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_ControllerDeviceEvent, event.type == SDL_CONTROLLERDEVICEADDED || event.type == SDL_CONTROLLERDEVICEREMOVED || event.type == SDL_CONTROLLERDEVICEREMAPPED, event.cdevice); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_DollarGestureEvent, event.type == SDL_DOLLARGESTURE || event.type == SDL_DOLLARRECORD, event.dgesture); +#if SDL_VERSION_ATLEAST(2, 0, 5) + SDL2PP_DEFINE_EVENT_MAPPING(SDL_DropEvent, event.type == SDL_DROPFILE || event.type == SDL_DROPTEXT || event.type == SDL_DROPBEGIN || event.type == SDL_DROPCOMPLETE, event.drop); +#else + SDL2PP_DEFINE_EVENT_MAPPING(SDL_DropEvent, event.type == SDL_DROPFILE, event.drop); +#endif + SDL2PP_DEFINE_EVENT_MAPPING(SDL_JoyAxisEvent, event.type == SDL_JOYAXISMOTION, event.jaxis); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_JoyBallEvent, event.type == SDL_JOYBALLMOTION, event.jball); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_JoyButtonEvent, event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP, event.jbutton); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_JoyDeviceEvent, event.type == SDL_JOYDEVICEADDED || event.type == SDL_JOYDEVICEREMOVED, event.jdevice); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_JoyHatEvent, event.type == SDL_JOYHATMOTION, event.jhat); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_KeyboardEvent, event.type == SDL_KEYDOWN || event.type == SDL_KEYUP, event.key); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_MouseButtonEvent, event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP, event.button); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_MouseMotionEvent, event.type == SDL_MOUSEMOTION, event.motion); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_MouseWheelEvent, event.type == SDL_MOUSEWHEEL, event.wheel); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_MultiGestureEvent, event.type == SDL_MULTIGESTURE, event.mgesture); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_QuitEvent, event.type == SDL_QUIT, event.quit); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_SysWMEvent, event.type == SDL_SYSWMEVENT, event.syswm); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_TextEditingEvent, event.type == SDL_TEXTEDITING, event.edit); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_TextInputEvent, event.type == SDL_TEXTINPUT, event.text); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_TouchFingerEvent, event.type == SDL_FINGERMOTION || event.type == SDL_FINGERDOWN || event.type == SDL_FINGERUP, event.tfinger); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_UserEvent, SDL_USEREVENT <= event.type && event.type <= SDL_LASTEVENT, event.user); + SDL2PP_DEFINE_EVENT_MAPPING(SDL_WindowEvent, event.type == SDL_WINDOWEVENT, event.window); + +#undef SDL2PP_DEFINE_EVENT_MAPPING +} +} + +#endif diff --git a/SDL2pp/Private/Utility.hh b/SDL2pp/Private/Utility.hh new file mode 100644 index 0000000..67e6403 --- /dev/null +++ b/SDL2pp/Private/Utility.hh @@ -0,0 +1,118 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2017 Vraiment + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL2PP_PRIVATE_UTILITY_HH +#define SDL2PP_PRIVATE_UTILITY_HH + +#if __cplusplus > 201703L +// No need to include utility, until C++17 there +// is no definition for std::disjuntion before +#include +#endif + +#include +#include + +namespace SDL2pp { +/* + * This is code not to be used directly by users of SDL2pp. + */ +namespace Private { +/* + * Templated class to perform an "OR" operation on any amount of types. + * + * Usage is as follows: + * template , HasFeatureTwo> + * void UseFeatureOneOrFeatureTwo(T t) { + * // The code will be only compiled if either + * // HasFeatureOne or HasFeatureTwo are type_true + * } + */ +#if __cplusplus >= 201703L + // Use the standard definitions if they are available + template + using Or = typename std::disjunction::type; +#else + template + struct OrOperation : std::false_type { }; + + template + struct OrOperation : T { }; + + template + struct OrOperation : std::conditional> { }; + + template + using Or = typename OrOperation::type; +#endif + +/* + * Templated class to perform an "AND" operation on any amount of types. + * + * Usage is as follows: + * template , HasFeatureTwo> + * void UseFeatureOneAndFeatureTwo(T t) { + * // The code will be only compiled if both + * // HasFeatureOne and HasFeatureTwo are type_true + * } + */ +#if __cplusplus >= 201703L + // Use the standard definitions if they are available + template + using And = typename std::conjunction::type; +#else + template + struct AndOperation : std::true_type { }; + + template + struct AndOperation : T { }; + + template + struct AndOperation : std::conditional, T> { }; + + template + using And = typename AndOperation::type; +#endif + +/* + * Templated class to an specific type in a tuple, returns std::true_type if the + * tuple contains T, std::false_type otherwise. + */ +template +struct TupleHasTypeOperation; + +template +struct TupleHasTypeOperation> : std::false_type { }; + +template +struct TupleHasTypeOperation> : TupleHasTypeOperation> { }; + +template +struct TupleHasTypeOperation> : std::true_type { }; + +template +using TupleHasType = typename TupleHasTypeOperation::type; +} +} + +#endif diff --git a/SDL2pp/SDL2pp.hh b/SDL2pp/SDL2pp.hh index 9a7ea16..6a92dbd 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -97,6 +97,14 @@ #include #include +/////////////////////////////////////////////////////////// +/// \defgroup events Event handling +/// +/// \brief Functions and classes to easily manage SDL events +/// +//////////////////////////////////////////////////////////// +#include + #ifdef SDL2PP_WITH_TTF //////////////////////////////////////////////////////////// /// \defgroup ttf SDL_ttf diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7d835f0..258457b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,10 +3,14 @@ set(CLI_TESTS test_color test_color_constexpr test_error + test_eventdispatching + test_eventhandler + test_eventpolling test_optional test_pointrect test_pointrect_constexpr test_rwops + test_utility test_wav ) diff --git a/tests/test_eventdispatching.cc b/tests/test_eventdispatching.cc new file mode 100644 index 0000000..feafe3d --- /dev/null +++ b/tests/test_eventdispatching.cc @@ -0,0 +1,156 @@ +#include + +#include + +#include "testing.h" + +using namespace SDL2pp::Private; + +namespace TestFreeFunctions { + static auto globalResult = Sint32{0}; + + 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.common.timestamp = 98; + event.type = SDL_QUIT; + event.user.code = 31; + + // Test dispatching with no event handler + DispatchEvent(event); + + // 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); + } + + // Test dispatching with function as event handler + { + DispatchEvent(event, TestFreeFunctions::handler); + EXPECT_EQUAL(event.user.code, TestFreeFunctions::globalResult); + } + + // Test dispatching with a functor as event handler + { + struct EventHandlerFunctor { + 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(); + + 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); + } + + // 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; + + SDL_Event keyboardEvent; + keyboardEvent.type = SDL_KEYUP; + + DispatchEvent(keyboardEvent, eventHandler); + + EXPECT_TRUE(!eventHandler.quitEventExecuted); + EXPECT_TRUE(eventHandler.keyboardEventExecuted); + } + + // Test call event handler that's both a functor and object + { + struct EventHandler { + bool quitEventFunctorExecuted = false; + bool quitEventObjectExecuted = false; + + void operator()(SDL_QuitEvent) { + quitEventFunctorExecuted = true; + } + + void HandleEvent(SDL_QuitEvent) { + quitEventObjectExecuted = true; + } + }; + + auto eventHandler = EventHandler(); + DispatchEvent(event, eventHandler); + + EXPECT_TRUE(eventHandler.quitEventFunctorExecuted); + EXPECT_TRUE(eventHandler.quitEventObjectExecuted); + } +END_TEST() diff --git a/tests/test_eventhandler.cc b/tests/test_eventhandler.cc new file mode 100644 index 0000000..d9be3a1 --- /dev/null +++ b/tests/test_eventhandler.cc @@ -0,0 +1,71 @@ +#include + +#include + +using namespace SDL2pp::Private; + +void EventHandlerFunction(SDL_Event); + +struct EventHandlerFunctor { + void operator()(SDL_Event); +}; + +struct EventHandler { + void HandleEvent(SDL_Event); +}; + +struct InvalidEventHandler { + void handleEvent(SDL_Event); +}; + +int main(int, char*[]) { + auto lambda = [](SDL_Event) { }; + + // Test IsEventHandlerFunctor + static_assert( + IsEventHandlerFunctor::value, + "IsEventHandlerFunctor<> should accept functions like void(SDL_Event)" + ); + static_assert( + IsEventHandlerFunctor::value, + "IsEventHandlerFunctor<> should accept functions like void(SDL_Event)" + ); + static_assert( + IsEventHandlerFunctor::value, + "IsEventHandlerFunctor<> shouldn accept a class with operator(SDL_Event)" + ); + + // Test IsEventHandlerObject + static_assert( + IsEventHandlerObject::value, + "IsEventHandlerObject<> should accept a class with HandleEvent(SDL_Event)" + ); + static_assert( + !IsEventHandlerObject::value, + "IsEventHandlerObject<> shouldn't accept a class without a valid signature" + ); + + // Test IsEventHandler + static_assert( + IsEventHandler::value, + "IsEventHandler<> should accept functions like void(SDL_Event)" + ); + static_assert( + IsEventHandler::value, + "IsEventHandler<> should accept functions like void(SDL_Event)" + ); + static_assert( + IsEventHandler::value, + "IsEventHandler<> should accept a class with operator(SDL_Event)" + ); + static_assert( + IsEventHandler::value, + "IsEventHandler<> should accept a class with HandleEvent(SDL_Event)" + ); + static_assert( + !IsEventHandler::value, + "IsEventHandler<> shouldn't accept a class without a valid signature" + ); + + return 0; +} diff --git a/tests/test_eventpolling.cc b/tests/test_eventpolling.cc new file mode 100644 index 0000000..72a2aaa --- /dev/null +++ b/tests/test_eventpolling.cc @@ -0,0 +1,76 @@ +#include + +#include +#include + +#include "testing.h" + +using namespace SDL2pp; +using namespace SDL2pp::Event; + +BEGIN_TEST(int, char*[]) + SDL sdl{SDL_INIT_EVENTS}; + + // Empty event poll + SDL_Event event; + while (SDL_PollEvent(&event)) { } + + // Poll a single event + { + event.type = SDL_KEYUP; + SDL_PushEvent(&event); + + EXPECT_TRUE(PollEvent()); + EXPECT_TRUE(!PollEvent()); // Verify no additional events + } + + // Poll multiple events single event + { + event.type = SDL_KEYUP; + constexpr auto amountOfEvents = 5; + for (auto n = 0; n < amountOfEvents; n++) + SDL_PushEvent(&event); + + EXPECT_EQUAL(PollAllEvents(), amountOfEvents); + EXPECT_TRUE(!PollEvent()); // Verify no additional events + } + + // Poll with an event handler + { + struct EventHandler { + int eventsHandled = 0; + int keyboardEventsHandled = 0; + bool quitEventHandled = false; + + void HandleEvent(SDL_Event) { + eventsHandled++; + } + + void HandleEvent(SDL_KeyboardEvent) { + keyboardEventsHandled++; + } + + void HandleEvent(SDL_QuitEvent) { + quitEventHandled = true; + } + }; + + event.type = SDL_KEYUP; + SDL_PushEvent(&event); + + event.type = SDL_KEYDOWN; + SDL_PushEvent(&event); + + event.type = SDL_QUIT; + SDL_PushEvent(&event); + + auto eventHandler = EventHandler(); + + EXPECT_EQUAL(PollAllEvents(eventHandler), 3); + EXPECT_TRUE(!PollEvent()); // Verify no additional events + + EXPECT_EQUAL(eventHandler.eventsHandled, 3); + EXPECT_EQUAL(eventHandler.keyboardEventsHandled, 2); + EXPECT_EQUAL(eventHandler.quitEventHandled, true); + } +END_TEST() diff --git a/tests/test_utility.cc b/tests/test_utility.cc new file mode 100644 index 0000000..ca420c3 --- /dev/null +++ b/tests/test_utility.cc @@ -0,0 +1,28 @@ +#include + +#include + +using namespace SDL2pp::Private; +using namespace std; + +int main(int, char*[]) { + static_assert(Or::value, "OR(true, true) should be true"); + static_assert(Or::value, "OR(true, false) should be true"); + static_assert(Or::value, "OR(false, true) should be true"); + static_assert(!Or::value, "OR(false, false) should be false"); + + static_assert(And::value, "AND(true, true) should be true"); + static_assert(!And::value, "AND(true, false) should be false"); + static_assert(!And::value, "AND(false, true) should be false"); + static_assert(!And::value, "AND(false, false) should be false"); + + struct A { }; + struct B { }; + struct C { }; + + static_assert(TupleHasType>::value, ""); + static_assert(TupleHasType>::value, ""); + static_assert(!TupleHasType>::value, ""); + + return 0; +}