| 102 | }; |
| 103 | |
| 104 | struct ApplicationSystem |
| 105 | { |
| 106 | ApplicationState state; |
| 107 | |
| 108 | HotReloadSystem hotReloadSystem; |
| 109 | HotReloadOptionsStorage hotReloadOptionsStorage; |
| 110 | |
| 111 | Result create() |
| 112 | { |
| 113 | currentThreadID = Thread::CurrentThreadID(); |
| 114 | lastEventTime.snap(); |
| 115 | SocketNetworking::initNetworking(); |
| 116 | AsyncEventLoop::Options options; |
| 117 | #if SC_PLATFORM_LINUX && 0 |
| 118 | // Just for testing purposes, we can force using epoll instead of io_uring |
| 119 | options.apiType = AsyncEventLoop::Options::ApiType::ForceUseEpoll; |
| 120 | #endif |
| 121 | SC_TRY(eventLoop.create(options)); |
| 122 | timeout.callback = [this](AsyncLoopTimeout::Result& result) { onTimeout(result); }; |
| 123 | SC_TRY(timeout.start(eventLoop, Time::Milliseconds(state.timeoutOccursEveryMs))); |
| 124 | eventLoopMonitor.onNewEventsAvailable = []() { sokol_wake_up(); }; |
| 125 | SC_TRY(eventLoopMonitor.create(eventLoop)); |
| 126 | SC_TRY(buildHotReloadOptions(hotReloadOptionsStorage)); |
| 127 | const HotReloadSystem::Options hotReloadOptions = {hotReloadOptionsStorage.examplesPath.view(), |
| 128 | hotReloadOptionsStorage.defaultIncludePathsText.view()}; |
| 129 | SC_TRY(hotReloadSystem.create(eventLoop, hotReloadOptions)); |
| 130 | SC_TRY(hotReloadSystem.syncRegistry()); |
| 131 | return Result(true); |
| 132 | } |
| 133 | |
| 134 | void resetLastEventTime() { lastEventTime.snap(); } |
| 135 | |
| 136 | // This is called during "frame callback" and it will either quickly execute or block when it's time to sleep |
| 137 | Result runLoopStepInsideSokolApp() |
| 138 | { |
| 139 | // Update loop time, mainly to display it in the GUI |
| 140 | state.loopTime = eventLoop.getLoopTime(); |
| 141 | |
| 142 | // Check if enough time has passed since last user input event |
| 143 | const Time::Relative sinceLastEvent = Time::HighResolutionCounter().snap().subtractApproximate(lastEventTime); |
| 144 | if (sinceLastEvent > Time::Milliseconds(state.continueDrawingForMs)) |
| 145 | { |
| 146 | // Enough time has passed such that we need to pause execution to avoid unnecessary cpu usage |
| 147 | if (state.pausedCounter < ApplicationState::NumPauseFrames) |
| 148 | { |
| 149 | state.pausedCounter++; |
| 150 | // Additional frames are needed to draw "paused" before entering sleep |
| 151 | return hotReloadSystem.update(); |
| 152 | } |
| 153 | // If we are here we really want to sleep the app until a new input event OR an I/O event arrives |
| 154 | // We implement this logic by: |
| 155 | // 1. Starting to monitor the event loop for IO in a different thread |
| 156 | // 2. Blocking on sokol native gui event loop (sokol_sleep) |
| 157 | // 2a. If input from user occurs, sokol_sleep() will unblock itself |
| 158 | // 2b. If I/O event occurs, calling sokol_wake_up() from the monitoring thread will unblock sokol_sleep |
| 159 | // 3. After returning from sokol_sleep, we make sure to dispatch callbacks for all ready completions |
| 160 | auto resetLastEventTime = MakeDeferred([this] { lastEventTime.snap(); }); |
| 161 | SC_TRY(eventLoopMonitor.startMonitoring()); |
nothing calls this directly
no outgoing calls
no test coverage detected