diff --git a/CMakeLists.txt b/CMakeLists.txt index 51eb91e..32ccd15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,8 @@ SET(LIBRARY_SOURCES SDL2pp/AudioLock.cc SDL2pp/AudioSpec.cc SDL2pp/Color.cc + SDL2pp/EventPolling.cc + SDL2pp/EventWait.cc SDL2pp/Exception.cc SDL2pp/Point.cc SDL2pp/RWops.cc @@ -145,6 +147,8 @@ SET(LIBRARY_HEADERS SDL2pp/AudioSpec.hh SDL2pp/Color.hh SDL2pp/ContainerRWops.hh + SDL2pp/EventPolling.hh + SDL2pp/EventWait.hh SDL2pp/Exception.hh SDL2pp/Optional.hh SDL2pp/Point.hh diff --git a/SDL2pp/EventPolling.cc b/SDL2pp/EventPolling.cc new file mode 100644 index 0000000..9df99a6 --- /dev/null +++ b/SDL2pp/EventPolling.cc @@ -0,0 +1,60 @@ +/* + 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. +*/ + +#include + +using std::function; + +namespace SDL2pp { + namespace Event { + bool PollEvent() { + SDL_Event event; + auto result = SDL_PollEvent(&event); + + return result; + } + + bool PollEvent(function eventHandler) { + SDL_Event event; + if (!SDL_PollEvent(&event)) { + return false; + } + + eventHandler(event); + + return true; + } + + int PollAllEvents() { + int result; + + for (result = 0; PollEvent(); result++); + + return result; + } + + int PollAllEvents(function eventHandler) { + int result; + for (result = 0; PollEvent(eventHandler); result++); + return result; + } + } +} diff --git a/SDL2pp/EventPolling.hh b/SDL2pp/EventPolling.hh new file mode 100644 index 0000000..eed2d2b --- /dev/null +++ b/SDL2pp/EventPolling.hh @@ -0,0 +1,144 @@ +/* + 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 + +#include + +#include + +namespace SDL2pp { + namespace Event { + //////////////////////////////////////////////////////////// + /// \brief Polls a single event + /// + /// This function tries to poll a single event from the event + /// queue using SDL_PollEvent(). If an event was polled it + /// returns true, if not it returns false. + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventPolling.hh + /// + /// \returns True if an event was polled, false otherwise + /// + /// \see https://wiki.libsdl.org/SDL_PollEvent + /// + //////////////////////////////////////////////////////////// + SDL2PP_EXPORT bool PollEvent(); + + //////////////////////////////////////////////////////////// + /// \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 free functions, lambdas and callable objects. + /// + /// If an instance of a callable struct/class needs to be passed + /// by reference, std::ref() needs to be used: + /// \code + /// struct EventHandler { + /// void operator()(const SDL_Event &); + /// }; + /// + /// EventHandler myEventHandler; + /// PollEvent(std::ref(myEventHandler)); + /// \endcode + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventPolling.hh + /// + /// \param[in] eventHandler Object that can be used as eventHandler(event) + /// where event is an instance of SDL_Event + /// + /// \returns True if an event was polled, false otherwise + /// + /// \see https://wiki.libsdl.org/SDL_Event + /// \see https://wiki.libsdl.org/SDL_PollEvent + /// + //////////////////////////////////////////////////////////// + SDL2PP_EXPORT bool PollEvent(std::function eventHandler); + + //////////////////////////////////////////////////////////// + /// \brief Polls all the events from the event queue + /// + /// This function calls SDL_PollEvent() until the event queue is empty. + /// Returns the amount of events that were polled from the queue. + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventPolling.hh + /// + /// \returns The amount of polled events (can be zero) + /// + /// \see https://wiki.libsdl.org/SDL_PollEvent + /// + //////////////////////////////////////////////////////////// + SDL2PP_EXPORT int PollAllEvents(); + + //////////////////////////////////////////////////////////// + /// \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 free functions, lambdas and callable objects. + /// + /// If an instance of a callable struct/class needs to be passed + /// by reference, std::ref() needs to be used: + /// \code + /// struct EventHandler { + /// void operator()(const SDL_Event &); + /// }; + /// + /// EventHandler myEventHandler; + /// PollAllEvents(std::ref(myEventHandler)); + /// \endcode + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventPolling.hh + /// + /// \param[in] eventHandler Object that can be used as eventHandler(event) + /// where event is an instance of SDL_Event + /// + /// \returns The amount of polled events (can be zero) + /// + /// \see https://wiki.libsdl.org/SDL_Event + /// \see https://wiki.libsdl.org/SDL_PollEvent + /// + //////////////////////////////////////////////////////////// + SDL2PP_EXPORT int PollAllEvents(std::function eventHandler); + } +} + +#endif diff --git a/SDL2pp/EventWait.cc b/SDL2pp/EventWait.cc new file mode 100644 index 0000000..6d59407 --- /dev/null +++ b/SDL2pp/EventWait.cc @@ -0,0 +1,53 @@ +/* + 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. +*/ + +#include + +#include + +namespace SDL2pp { + namespace Event { + SDL_Event WaitEvent() { + SDL_Event result; + if (SDL_WaitEvent(&result) == 0) { + throw Exception("SDL_WaitEvent"); + } + return result; + } + + Optional WaitEvent(int timeout) { + SDL_Event result; + SDL_ClearError(); // Necessary to ensure SDL_WaitEventTimeout + // returned with no errors + if (SDL_WaitEventTimeout(&result, timeout) == 0) { + auto error = SDL_GetError(); + // Check if the error message is empty + if (*error == 0) { + return Optional(); + } else { + throw Exception("SDL_WaitEventTimeout"); + } + } + + return result; + } + } +} diff --git a/SDL2pp/EventWait.hh b/SDL2pp/EventWait.hh new file mode 100644 index 0000000..c8a3e42 --- /dev/null +++ b/SDL2pp/EventWait.hh @@ -0,0 +1,77 @@ +/* + 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_EVENTWAIT_HH +#define SDL2PP_EVENTWAIT_HH + +#include + +#include +#include + +namespace SDL2pp { + namespace Event { + //////////////////////////////////////////////////////////// + /// \brief Waits indefinitely for an event + /// + /// This function waits for the next available event. This + /// function WILL STOP the program execution until an event + /// is available. + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventWait.hh + /// + /// \returns The event that was available + /// + /// \throws SDL2pp::Exception + /// + /// \see https://wiki.libsdl.org/SDL_WaitEvent + /// + //////////////////////////////////////////////////////////// + SDL2PP_EXPORT SDL_Event WaitEvent(); + + //////////////////////////////////////////////////////////// + /// \brief Waits a given amount of time for an event + /// + /// This function waits for the next available event for + /// specified amount of time. This function WILL STOP the + /// program execution until the given amount of time runs out + /// or an event is available. + /// + /// \ingroup events + /// + /// \headerfile SDL2pp/EventWait.hh + /// + /// \param[in] timeout The amount of time to wait for an event + /// + /// \returns The last event that was available + /// + /// \throws SDL2pp::Exception + /// + /// \see https://wiki.libsdl.org/SDL_WaitEvent + /// + //////////////////////////////////////////////////////////// + SDL2PP_EXPORT Optional WaitEvent(int timeout); + } +} + +#endif diff --git a/SDL2pp/SDL2pp.hh b/SDL2pp/SDL2pp.hh index 22110b4..340d3a2 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -97,6 +97,15 @@ #include #include +//////////////////////////////////////////////////////////// +/// \defgroup events Event handling +/// +/// \brief Functions and classes to easily manage SDL events +/// +//////////////////////////////////////////////////////////// +#include +#include + #ifdef SDL2PP_WITH_TTF //////////////////////////////////////////////////////////// /// \defgroup ttf SDL_ttf diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1c14256..22646aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,8 @@ SET(CLI_TESTS test_color test_color_constexpr + test_eventpolling + test_eventwait test_error test_optional test_pointrect diff --git a/tests/test_eventpolling.cc b/tests/test_eventpolling.cc new file mode 100644 index 0000000..a7a662e --- /dev/null +++ b/tests/test_eventpolling.cc @@ -0,0 +1,213 @@ +#include + +#include +#include + +#include "testing.h" + +#include + +using namespace SDL2pp; +using namespace SDL2pp::Event; +using namespace std; + +inline SDL_Event PushUserEvent(Sint32 userCode = 0, void *data1 = nullptr, void *data2 = nullptr) { + SDL_Event event; + event.type = SDL_USEREVENT; + event.user.code = userCode; + event.user.data1 = data1; + event.user.data2 = data2; + + if (SDL_PushEvent(&event) < 0) { + throw Exception("SDL_PushEvent"); + } + + return event; +} + +namespace StaticEventHandler { + vector events; + + inline void EventHandler(const SDL_Event& event) { + events.push_back(event); + } +} + +struct CallableEventHandler { + vector events; + + void operator()(const SDL_Event& event) { + events.push_back(event); + } +}; + +BEGIN_TEST(int, char*[]) + // With no callback and no polled event + { + EXPECT_TRUE(PollEvent() == false); + } + + // With no callback and a polled event + { + PushUserEvent(); + + EXPECT_TRUE(PollEvent() == true); + EXPECT_TRUE(PollEvent() == false); + } + + + // With function as an event handler + { + StaticEventHandler::events.clear(); + + const SDL_Event expected = PushUserEvent(15); + + EXPECT_TRUE(PollEvent(StaticEventHandler::EventHandler) == true); + EXPECT_TRUE(StaticEventHandler::events.size() == 1); + + const SDL_Event result = StaticEventHandler::events[0]; + EXPECT_TRUE(result.type == expected.type); + EXPECT_TRUE(result.user.code == expected.user.code); + + // Verify no further events + EXPECT_TRUE(PollEvent(StaticEventHandler::EventHandler) == false); + EXPECT_TRUE(StaticEventHandler::events.size() == 1); + } + + // With lambda as an event handler + { + vector events; + const SDL_Event expectedEvent = PushUserEvent(22); + + EXPECT_TRUE(PollEvent([&events](const SDL_Event& event) { + events.push_back(event); + }) == true); + EXPECT_TRUE(events.size() == 1); + + const SDL_Event result = events[0]; + EXPECT_TRUE(result.type == expectedEvent.type); + EXPECT_TRUE(result.user.code == expectedEvent.user.code); + + // Verify no further events + EXPECT_TRUE(PollEvent([&events](const SDL_Event& event) { + events.push_back(event); + }) == false); + EXPECT_TRUE(events.size() == 1); + } + + // With callable object + { + CallableEventHandler eventHandler; + + const SDL_Event expectedEvent = PushUserEvent(45); + + EXPECT_TRUE(PollEvent(std::ref(eventHandler)) == true); + EXPECT_TRUE(eventHandler.events.size() == 1); + + const SDL_Event result = eventHandler.events[0]; + EXPECT_TRUE(result.type == expectedEvent.type); + EXPECT_TRUE(result.user.code == expectedEvent.user.code); + + // Verify no further events + EXPECT_TRUE(PollEvent(eventHandler) == false); + EXPECT_TRUE(eventHandler.events.size() == 1); + } + + // With no callback and no polled events + { + EXPECT_TRUE(PollAllEvents() == 0); + } + + // With no callback and several polled events + { + constexpr int totalEvents = 5; + for (int n = 0; n < totalEvents; ++n) { + PushUserEvent(); + } + + EXPECT_TRUE(PollAllEvents() == totalEvents); + // Verify no further events + EXPECT_TRUE(PollEvent() == false); + } + + // With function as an event handler + { + StaticEventHandler::events.clear(); + + vector expectedEvents; + for (const auto eventCode : { 15, 32, 99, 85 }) { + expectedEvents.push_back(PushUserEvent(eventCode)); + } + int totalExpectedEvents = static_cast(expectedEvents.size()); + + EXPECT_TRUE(PollAllEvents(StaticEventHandler::EventHandler) == totalExpectedEvents); + EXPECT_TRUE(StaticEventHandler::events.size() == expectedEvents.size()); + + for (int n = 0; n < totalExpectedEvents; n++) { + const SDL_Event result = StaticEventHandler::events[n]; + const SDL_Event expected = expectedEvents[n]; + + EXPECT_TRUE(result.type == expected.type); + EXPECT_TRUE(result.user.code == expected.user.code); + } + + // Verify no further events + EXPECT_TRUE(PollEvent(StaticEventHandler::EventHandler) == false); + EXPECT_TRUE(StaticEventHandler::events.size() == expectedEvents.size()); + } + + // With lambda as an event handler + { + vector handledEvents; + vector expectedEvents; + for (const auto eventCode : { 37, 88, 42, 63, 23, 19 }) { + expectedEvents.push_back(PushUserEvent(eventCode)); + } + int totalExpectedEvents = static_cast(expectedEvents.size()); + + EXPECT_TRUE(PollAllEvents([&handledEvents](const SDL_Event& event) { + handledEvents.push_back(event); + }) == totalExpectedEvents); + EXPECT_TRUE(handledEvents.size() == expectedEvents.size()); + + for (int n = 0; n < totalExpectedEvents; n++) { + const SDL_Event result = handledEvents[n]; + const SDL_Event expected = expectedEvents[n]; + + EXPECT_TRUE(result.type == expected.type); + EXPECT_TRUE(result.user.code == expected.user.code); + } + + // Verify no further events + EXPECT_TRUE(PollEvent([&handledEvents](const SDL_Event& event) { + handledEvents.push_back(event); + }) == false); + EXPECT_TRUE(handledEvents.size() == expectedEvents.size()); + } + + // With callable object as an event handler + { + CallableEventHandler eventHandler; + + vector expectedEvents; + for (const auto eventCode : { 11, 98, 62, 35, 71 }) { + expectedEvents.push_back(PushUserEvent(eventCode)); + } + int totalExpectedEvents = static_cast(expectedEvents.size()); + + EXPECT_TRUE(PollAllEvents(std::ref(eventHandler)) == totalExpectedEvents); + EXPECT_TRUE(eventHandler.events.size() == expectedEvents.size()); + + for (int n = 0; n < totalExpectedEvents; n++) { + const SDL_Event result = eventHandler.events[n]; + const SDL_Event expected = expectedEvents[n]; + + EXPECT_TRUE(result.type == expected.type); + EXPECT_TRUE(result.user.code == expected.user.code); + } + + // Verify no further events + EXPECT_TRUE(PollEvent(eventHandler) == false); + EXPECT_TRUE(eventHandler.events.size() == expectedEvents.size()); + } +END_TEST() diff --git a/tests/test_eventwait.cc b/tests/test_eventwait.cc new file mode 100644 index 0000000..4e3ea8b --- /dev/null +++ b/tests/test_eventwait.cc @@ -0,0 +1,44 @@ +#include + +#include +#include + +#include "testing.h" + +using namespace SDL2pp; +using namespace SDL2pp::Event; + +BEGIN_TEST(int, char*[]) + const SDL sdl(SDL_INIT_EVENTS); + + // Test wait event with no timeout + { + SDL_Event expected; + expected.type = SDL_USEREVENT; + expected.user.code = 99; + SDL_PushEvent(&expected); + + auto result = WaitEvent(); + EXPECT_TRUE(result.type == expected.type); + EXPECT_TRUE(result.user.code == expected.user.code); + } + + // Test wait event with timeout and with event + { + SDL_Event expected; + expected.type = SDL_USEREVENT; + expected.user.code = 32; + SDL_PushEvent(&expected); + + auto result = WaitEvent(10); + EXPECT_TRUE(!(result == NullOpt)); + EXPECT_TRUE(result->type == expected.type); + EXPECT_TRUE(result->user.code == expected.user.code); + } + + // Test wait event with timeout and no event + { + auto result = WaitEvent(10); + EXPECT_TRUE(result == NullOpt); + } +END_TEST()