diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index daef9930..a30d3c6e 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -91,6 +91,9 @@ class LIBSCRATCHCPP_EXPORT IEngine */ virtual void runEventLoop() = 0; + /*! Stops the event loop which is running in another thread. */ + virtual void stopEventLoop() = 0; + /*! Sets the function which is called on every frame. */ virtual void setRedrawHandler(const std::function &handler) = 0; diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index d221dba2..2473119a 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -322,6 +322,13 @@ void Engine::runEventLoop() eventLoop(); } +void Engine::stopEventLoop() +{ + m_stopEventLoopMutex.lock(); + m_stopEventLoop = true; + m_stopEventLoopMutex.unlock(); +} + void Engine::setRedrawHandler(const std::function &handler) { m_redrawHandler = handler; @@ -331,6 +338,7 @@ void Engine::eventLoop(bool untilProjectStops) { updateFrameDuration(); m_newScripts.clear(); + m_stopEventLoop = false; while (true) { auto frameStart = m_clock->currentSteadyTime(); @@ -369,6 +377,16 @@ void Engine::eventLoop(bool untilProjectStops) } } + // Stop the event loop if stopEventLoop() was called + m_stopEventLoopMutex.lock(); + + if (m_stopEventLoop) { + stop = true; + break; + } + + m_stopEventLoopMutex.unlock(); + currentTime = m_clock->currentSteadyTime(); elapsedTime = std::chrono::duration_cast(currentTime - frameStart); sleepTime = m_frameDuration - elapsedTime; diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h index b7fceeb1..33ebbc3b 100644 --- a/src/engine/internal/engine.h +++ b/src/engine/internal/engine.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "blocksectioncontainer.h" @@ -37,8 +38,10 @@ class Engine : public IEngine void stopTarget(Target *target, VirtualMachine *exceptScript) override; void initClone(libscratchcpp::Sprite *clone) override; void deinitClone(libscratchcpp::Sprite *clone) override; + void run() override; void runEventLoop() override; + void stopEventLoop() override; void setRedrawHandler(const std::function &handler) override; @@ -183,6 +186,8 @@ class Engine : public IEngine bool m_running = false; bool m_redrawRequested = false; std::function m_redrawHandler = nullptr; + bool m_stopEventLoop = false; + std::mutex m_stopEventLoopMutex; }; } // namespace libscratchcpp diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp index 39b458b5..1d0dc12e 100644 --- a/test/engine/engine_test.cpp +++ b/test/engine/engine_test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../common.h" #include "testsection.h" @@ -68,6 +69,16 @@ TEST(EngineTest, IsRunning) ASSERT_FALSE(engine.isRunning()); } +TEST(EngineTest, EventLoop) +{ + Engine engine; + + std::thread th([&engine]() { engine.runEventLoop(); }); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + engine.stopEventLoop(); + th.join(); // should return immediately +} + TEST(EngineTest, Fps) { Engine engine; diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index 9b83bc6f..a8be5fc1 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -23,8 +23,10 @@ class EngineMock : public IEngine MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override)); MOCK_METHOD(void, initClone, (Sprite *), (override)); MOCK_METHOD(void, deinitClone, (Sprite *), (override)); + MOCK_METHOD(void, run, (), (override)); MOCK_METHOD(void, runEventLoop, (), (override)); + MOCK_METHOD(void, stopEventLoop, (), (override)); MOCK_METHOD(void, setRedrawHandler, (const std::function &), (override));