diff --git a/include/tvision/internal/dispbuff.h b/include/tvision/internal/dispbuff.h index f89f2b57..d529c4b6 100644 --- a/include/tvision/internal/dispbuff.h +++ b/include/tvision/internal/dispbuff.h @@ -22,6 +22,9 @@ class DisplayBuffer { friend FlushScreenAlgorithm; + using Clock = std::chrono::steady_clock; + using TimePoint = Clock::time_point; + struct Range { int begin, end; }; @@ -42,7 +45,8 @@ class DisplayBuffer bool limitFPS; std::chrono::microseconds flushDelay {}; - std::chrono::time_point lastFlush {}; + TimePoint lastFlush {}; + TimePoint pendingFlush {}; #ifdef _WIN32 static constexpr int defaultFPS = 120; // Just 60 feels notably slower on Windows, I don't know why. @@ -79,6 +83,8 @@ class DisplayBuffer void setCursorPosition(int x, int y) noexcept; void setCursorVisibility(bool visible) noexcept; + + int timeUntilPendingFlushMs() noexcept; }; inline bool DisplayBuffer::inBounds(int x, int y) const noexcept diff --git a/include/tvision/internal/platform.h b/include/tvision/internal/platform.h index 7d26a46e..dfcc2a29 100644 --- a/include/tvision/internal/platform.h +++ b/include/tvision/internal/platform.h @@ -111,7 +111,7 @@ class Platform { console.lock([&] (auto *&c) { this->restoreConsole(c); }); } bool getEvent(TEvent &ev) noexcept; - void waitForEvents(int ms) noexcept { checkConsole(); waiter.waitForEvents(ms); } + void waitForEvents(int ms) noexcept; void interruptEventWait() noexcept { waiter.interruptEventWait(); } int getButtonCount() noexcept diff --git a/source/platform/dispbuff.cpp b/source/platform/dispbuff.cpp index 0c0e0d16..8006ad10 100644 --- a/source/platform/dispbuff.cpp +++ b/source/platform/dispbuff.cpp @@ -6,8 +6,6 @@ #include #include #include -using std::chrono::microseconds; -using std::chrono::steady_clock; #ifdef _MSC_VER #define __builtin_expect(x, y) x @@ -28,7 +26,7 @@ DisplayBuffer::DisplayBuffer() noexcept : int fps = getEnv("TVISION_MAX_FPS", defaultFPS); limitFPS = (fps > 0); if (limitFPS) - flushDelay = microseconds((int) 1e6/fps); + flushDelay = std::chrono::microseconds((int) 1e6/fps); } TScreenCell *DisplayBuffer::reloadScreenInfo(DisplayStrategy &display) noexcept @@ -119,15 +117,34 @@ bool DisplayBuffer::timeToFlush() noexcept // Avoid flushing faster than the maximum FPS. if (limitFPS) { - auto now = steady_clock::now(); - if (now - lastFlush >= flushDelay) + auto now = Clock::now(); + auto flushTime = lastFlush + flushDelay; + if (flushTime <= now) + { lastFlush = now; + pendingFlush = TimePoint(); + } else + { + pendingFlush = flushTime; return false; + } } return true; } +int DisplayBuffer::timeUntilPendingFlushMs() noexcept +{ + using namespace std::chrono; + if (pendingFlush != TimePoint()) + { + auto time = pendingFlush - Clock::now(); + if (time >= milliseconds::zero()) + return duration_cast(time).count(); + } + return -1; +} + void DisplayBuffer::setCursorPosition(int x, int y) noexcept { TPoint pos {x, y}; diff --git a/source/platform/platfcon.cpp b/source/platform/platfcon.cpp index e7f4359a..8bec3929 100644 --- a/source/platform/platfcon.cpp +++ b/source/platform/platfcon.cpp @@ -71,6 +71,22 @@ bool Platform::getEvent(TEvent &ev) noexcept return false; } +void Platform::waitForEvents(int ms) noexcept +{ + checkConsole(); + + int waitTimeoutMs = ms; + // When the DisplayBuffer has pending changes, ensure we wake up so that + // they can be flushed in time. + int flushTimeoutMs = displayBuf.timeUntilPendingFlushMs(); + if (ms < 0) + waitTimeoutMs = flushTimeoutMs; + else if (flushTimeoutMs >= 0) + waitTimeoutMs = min(ms, flushTimeoutMs); + + waiter.waitForEvents(waitTimeoutMs); +} + void Platform::signalCallback(bool enter) noexcept { if (instance && !instance->console.lockedByCurrentThread())