diff --git a/Source/interfac.cpp b/Source/interfac.cpp index 33777b571fb..94144901f09 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -37,6 +37,23 @@ namespace devilution { namespace { +#if defined(__APPLE__) && defined(USE_SDL1) +// On Tiger PPC, SDL_PushEvent from a background thread appears to do nothing. +#define SDL_PUSH_EVENT_BG_THREAD_WORKS 0 +#else +#define SDL_PUSH_EVENT_BG_THREAD_WORKS 1 +#endif + +#if !SDL_PUSH_EVENT_BG_THREAD_WORKS +// This workaround is not completely thread-safe but the worst +// that can happen is we miss some WM_PROGRESS events, +// which is not a problem. +struct { + std::atomic type; + std::string error; +} NextCustomEvent; +#endif + constexpr uint32_t MaxProgress = 534; constexpr uint32_t ProgressStepSize = 23; @@ -391,16 +408,31 @@ void DoLoad(interface_mode uMsg) } if (!loadResult.has_value()) { +#if SDL_PUSH_EVENT_BG_THREAD_WORKS SDL_Event event; event.type = CustomEventToSdlEvent(WM_ERROR); event.user.data1 = new std::string(std::move(loadResult).error()); - SDL_PushEvent(&event); + if (SDL_PushEvent(&event) < 0) { + LogError("Failed to send WM_ERROR {}", SDL_GetError()); + SDL_ClearError(); + } +#else + NextCustomEvent.error = std::move(loadResult).error(); + NextCustomEvent.type = static_cast(WM_ERROR); +#endif return; } +#if SDL_PUSH_EVENT_BG_THREAD_WORKS SDL_Event event; event.type = CustomEventToSdlEvent(WM_DONE); - SDL_PushEvent(&event); + if (SDL_PushEvent(&event) < 0) { + LogError("Failed to send WM_DONE {}", SDL_GetError()); + SDL_ClearError(); + } +#else + NextCustomEvent.type = static_cast(WM_DONE); +#endif } struct { @@ -561,9 +593,16 @@ void IncProgress(uint32_t steps) if (sgdwProgress > MaxProgress) sgdwProgress = MaxProgress; if (!HeadlessMode && sgdwProgress != prevProgress) { +#if SDL_PUSH_EVENT_BG_THREAD_WORKS SDL_Event event; event.type = CustomEventToSdlEvent(WM_PROGRESS); - SDL_PushEvent(&event); + if (SDL_PushEvent(&event) < 0) { + LogError("Failed to send WM_PROGRESS {}", SDL_GetError()); + SDL_ClearError(); + } +#else + NextCustomEvent.type = static_cast(WM_PROGRESS); +#endif } } @@ -589,6 +628,10 @@ void ShowProgress(interface_mode uMsg) ProgressEventHandlerState.done = false; ProgressEventHandlerState.drawnProgress = 0; +#if !SDL_PUSH_EVENT_BG_THREAD_WORKS + NextCustomEvent.type = -1; +#endif + #ifndef USE_SDL1 DeactivateVirtualGamepad(); FreeVirtualGamepadTextures(); @@ -623,21 +666,35 @@ void ShowProgress(interface_mode uMsg) LogVerbose("Load thread finished in {}ms", SDL_GetTicks() - start); }); + const auto processEvent = [&](const SDL_Event &event) { + CheckShouldSkipRendering(); + if (event.type != SDL_QUIT) { + HandleMessage(event, SDL_GetModState()); + } + if (ProgressEventHandlerState.done) { + loadThread.join(); + return false; + } + return true; + }; + while (true) { CheckShouldSkipRendering(); SDL_Event event; // We use the real `SDL_PollEvent` here instead of `FetchEvent` // to process real events rather than the recorded ones in demo mode. while (SDL_PollEvent(&event)) { - CheckShouldSkipRendering(); - if (event.type != SDL_QUIT) { - HandleMessage(event, SDL_GetModState()); - } - if (ProgressEventHandlerState.done) { - loadThread.join(); - return; + if (!processEvent(event)) return; + } +#if !SDL_PUSH_EVENT_BG_THREAD_WORKS + if (const int customEventType = NextCustomEvent.type.exchange(-1); customEventType != -1) { + event.type = CustomEventToSdlEvent(static_cast(customEventType)); + if (static_cast(customEventType) == static_cast(WM_ERROR)) { + event.user.data1 = &NextCustomEvent.error; } + if (!processEvent(event)) return; } +#endif } }