Add timeout parameter to waitEvent

This commit is contained in:
vittorioromeo 2024-06-14 13:53:58 +02:00 committed by Vittorio Romeo
parent 51efe50ec4
commit e7b23ffcd1
5 changed files with 114 additions and 44 deletions

View File

@ -33,6 +33,7 @@
#include <SFML/Window/WindowEnums.hpp>
#include <SFML/Window/WindowHandle.hpp>
#include <SFML/System/Time.hpp>
#include <SFML/System/Vector2.hpp>
#include <memory>
@ -202,11 +203,12 @@ public:
/// \brief Wait for an event and return it
///
/// This function is blocking: if there's no pending event then
/// it will wait until an event is received. After this function
/// returns if no error occurred, the returned event will not be
/// empty. This function is typically used when you have a thread
/// that is dedicated to events handling: you want to make this
/// thread sleep as long as no new event is received.
/// it will wait until an event is received or until the provided
/// timeout elapses. After this function returns if no error nor
/// timeout occurred, the returned event will not be empty.
/// This function is typically used when you have a thread that is
/// dedicated to events handling: you want to make this thread sleep
/// as long as no new event is received.
/// \code
/// if (const auto event = window.waitEvent())
/// {
@ -214,12 +216,14 @@ public:
/// }
/// \endcode
///
/// \return The event
/// \param timeout Maximum time to wait (`Time::Zero` for infinite)
///
/// \return The event; will be `Empty` (convertible to `false`) on timeout or if window was closed
///
/// \see pollEvent
///
////////////////////////////////////////////////////////////
[[nodiscard]] Event waitEvent();
[[nodiscard]] Event waitEvent(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Get the position of the window

View File

@ -149,17 +149,17 @@ bool WindowBase::isOpen() const
Event WindowBase::pollEvent()
{
Event event;
if (m_impl && (event = m_impl->popEvent(false)))
if (m_impl && (event = m_impl->pollEvent()))
filterEvent(event);
return event;
}
////////////////////////////////////////////////////////////
Event WindowBase::waitEvent()
Event WindowBase::waitEvent(Time timeout)
{
Event event;
if (m_impl && (event = m_impl->popEvent(true)))
if (m_impl && (event = m_impl->waitEvent(timeout)))
filterEvent(event);
return event;
}

View File

@ -35,6 +35,7 @@
#include <SFML/System/Time.hpp>
#include <array>
#include <chrono>
#include <memory>
#include <cmath>
@ -175,35 +176,46 @@ void WindowImpl::setMaximumSize(const std::optional<Vector2u>& maximumSize)
////////////////////////////////////////////////////////////
Event WindowImpl::popEvent(bool block)
Event WindowImpl::waitEvent(Time timeout)
{
const auto timedOut = [&, startTime = std::chrono::steady_clock::now()]
{
const bool infiniteTimeout = timeout == Time::Zero;
return !infiniteTimeout && (std::chrono::steady_clock::now() - startTime) >= timeout.toDuration();
};
// If the event queue is empty, let's first check if new events are available from the OS
if (m_events.empty())
populateEventQueue();
// Here we use a manual wait loop instead of the optimized wait-event provided by the OS,
// so that we don't skip joystick events (which require polling)
while (m_events.empty() && !timedOut())
{
sleep(milliseconds(10));
populateEventQueue();
}
return popEvent();
}
////////////////////////////////////////////////////////////
Event WindowImpl::pollEvent()
{
// If the event queue is empty, let's first check if new events are available from the OS
if (m_events.empty())
{
// Get events from the system
processJoystickEvents();
processSensorEvents();
processEvents();
populateEventQueue();
// In blocking mode, we must process events until one is triggered
if (block)
{
// Here we use a manual wait loop instead of the optimized
// wait-event provided by the OS, so that we don't skip joystick
// events (which require polling)
while (m_events.empty())
{
sleep(milliseconds(10));
processJoystickEvents();
processSensorEvents();
processEvents();
}
}
return popEvent();
}
////////////////////////////////////////////////////////////
Event WindowImpl::popEvent()
{
Event event;
// Pop the first event of the queue, if it is not empty
if (!m_events.empty())
{
event = m_events.front();
@ -311,6 +323,15 @@ void WindowImpl::processSensorEvents()
}
////////////////////////////////////////////////////////////
void WindowImpl::populateEventQueue()
{
processJoystickEvents();
processSensorEvents();
processEvents();
}
////////////////////////////////////////////////////////////
bool WindowImpl::createVulkanSurface([[maybe_unused]] const VkInstance& instance,
[[maybe_unused]] VkSurfaceKHR& surface,

View File

@ -55,6 +55,7 @@
namespace sf
{
class String;
class Time;
namespace priv
{
@ -121,21 +122,28 @@ public:
void setJoystickThreshold(float threshold);
////////////////////////////////////////////////////////////
/// \brief Return the next window event available
/// \brief Wait for and return the next available window event
///
/// If there's no event available, this function calls the
/// window's internal event processing function.
/// The \a block parameter controls the behavior of the function
/// if no event is available: if it is true then the function
/// doesn't return until a new event is triggered; otherwise it
/// returns an empty event to indicate that no event is available.
///
/// \param block Use true to block the thread until an event arrives
/// \param timeout Maximum time to wait (`Time::Zero` for infinite)
///
/// \return The event; can be `Empty` (convertible to `false`) if not blocking
/// \return The event on success, `Event::Empty` otherwise
///
////////////////////////////////////////////////////////////
Event popEvent(bool block);
[[nodiscard]] Event waitEvent(Time timeout);
////////////////////////////////////////////////////////////
/// \brief Return the next window event, if available
///
/// If there's no event available, this function calls the
/// window's internal event processing function.
///
/// \return The event if available, `Event::Empty` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] Event pollEvent();
////////////////////////////////////////////////////////////
/// \brief Get the OS-specific handle of the window
@ -325,6 +333,12 @@ protected:
private:
struct JoystickStatesImpl;
////////////////////////////////////////////////////////////
/// \brief Pop the first event of the queue if available, otherwise an empty event
///
////////////////////////////////////////////////////////////
[[nodiscard]] Event popEvent();
////////////////////////////////////////////////////////////
/// \brief Read the joysticks state and generate the appropriate events
///
@ -337,6 +351,12 @@ private:
////////////////////////////////////////////////////////////
void processSensorEvents();
////////////////////////////////////////////////////////////
/// \brief Read joystick, sensors, and OS state and populate event queue
///
////////////////////////////////////////////////////////////
void populateEventQueue();
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////

View File

@ -9,6 +9,7 @@
#include <catch2/catch_test_macros.hpp>
#include <WindowUtil.hpp>
#include <chrono>
#include <type_traits>
TEST_CASE("[Window] sf::WindowBase", runDisplayTests())
@ -103,17 +104,41 @@ TEST_CASE("[Window] sf::WindowBase", runDisplayTests())
}
SECTION("pollEvent()")
{
SECTION("Uninitialized window")
{
sf::WindowBase windowBase;
CHECK(!windowBase.pollEvent());
}
}
SECTION("waitEvent()")
{
SECTION("Uninitialized window")
{
sf::WindowBase windowBase;
CHECK(!windowBase.waitEvent());
}
SECTION("Initialized window")
{
sf::WindowBase windowBase(sf::VideoMode({360, 240}), "WindowBase Tests");
constexpr auto timeout = sf::milliseconds(100);
const auto startTime = std::chrono::steady_clock::now();
const auto event = windowBase.waitEvent(timeout);
const auto elapsed = std::chrono::steady_clock::now() - startTime;
REQUIRE(elapsed < (timeout + sf::milliseconds(50)).toDuration());
if (elapsed <= timeout.toDuration())
CHECK(event);
else
CHECK(!event);
}
}
SECTION("Set/get position")
{
sf::WindowBase windowBase;