From 39b743608707b4895fb8d7911a40d0ed5d270c07 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 00:14:49 +0300 Subject: [PATCH 01/28] Updating (fix) the auto-pause --- src/core/Game.cpp | 3 +++ src/core/Timer.cpp | 8 ++++---- src/skel/glfw/glfw.cpp | 28 +++++++++++++++++++++++++--- src/skel/win/win.cpp | 28 ++++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 3915b61..5879b2f 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -1255,6 +1255,9 @@ void CGame::ProcessTidyUpMemory(void) void CGame::InitAfterFocusLoss() { + if (CGame::playingIntro || CCutsceneMgr::IsRunning() || CCutsceneMgr::IsCutsceneProcessing()) + return; + FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = FrontEndMenuManager.m_lastWorking3DAudioProvider; DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_lastWorking3DAudioProvider); diff --git a/src/core/Timer.cpp b/src/core/Timer.cpp index 72272c9..d7d0ac2 100644 --- a/src/core/Timer.cpp +++ b/src/core/Timer.cpp @@ -94,11 +94,11 @@ void CTimer::Update(void) LARGE_INTEGER pc; QueryPerformanceCounter(&pc); - int32 updInCycles = (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless + int64 updInCycles = pc.QuadPart - _oldPerfCounter.QuadPart; _oldPerfCounter = pc; - float updInCyclesScaled = GetIsPaused() ? updInCycles : updInCycles * ms_fTimeScale; + double updInCyclesScaled = GetIsPaused() ? (double)updInCycles : (double)updInCycles * ms_fTimeScale; // We need that real frame time to fix transparent menu bug. #ifndef FIX_HIGH_FPS_BUGS_ON_FRONTEND @@ -190,7 +190,7 @@ void CTimer::Resume(void) LARGE_INTEGER pc; QueryPerformanceCounter(&pc); - _oldPerfCounter.LowPart += pc.LowPart - perfSuspendCounter.LowPart; + _oldPerfCounter.QuadPart += pc.QuadPart - perfSuspendCounter.QuadPart; } else #endif @@ -214,7 +214,7 @@ uint32 CTimer::GetCurrentTimeInCycles(void) { LARGE_INTEGER pc; QueryPerformanceCounter(&pc); - return (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless + return (uint32)(pc.QuadPart - _oldPerfCounter.QuadPart); } else #endif diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index a2d24cb..da9bd81 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -43,6 +43,7 @@ long _dwOperatingSystemVersion; #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" +#include "CutsceneMgr.h" #include "PCSave.h" #include "MemoryCard.h" #include "Sprite2d.h" @@ -113,6 +114,12 @@ IsWindowMinimizedPauseActive(void) return WindowIconified || (IsFullscreenLikeWindowMode() && !WindowFocused); } +static RwBool +IsFocusLossInCutscene(void) +{ + return CGame::playingIntro || CCutsceneMgr::IsRunning() || CCutsceneMgr::IsCutsceneProcessing(); +} + static void SetWindowAudioSuspended(RwBool suspend) { @@ -143,9 +150,17 @@ static void UpdateWindowMinimizedPause(void) { RwBool pause = IsWindowMinimizedPauseActive(); + if (pause && IsFocusLossInCutscene()) { + CTimer::SetWindowMinimizedPause(false); + return; + } + + bool wasPaused = CTimer::GetIsWindowMinimizedPaused(); CTimer::SetWindowMinimizedPause(pause); if (pause) { + if (!wasPaused) + CGame::InitAfterFocusLoss(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); } @@ -248,12 +263,17 @@ psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { - ForegroundApp = FALSE; - RefreshWindowActivityState(); - SetWindowAudioSuspended(TRUE); + if (!IsFocusLossInCutscene()) { + ForegroundApp = FALSE; + RefreshWindowActivityState(); + SetWindowAudioSuspended(TRUE); + } return FALSE; } + if (IsWindowMinimizedPauseActive() && !IsFocusLossInCutscene()) + UpdateWindowMinimizedPause(); + return TRUE; } @@ -1907,6 +1927,8 @@ cursorEnterCB(GLFWwindow* window, int entered) { void windowFocusCB(GLFWwindow* window, int focused) { WindowFocused = !!focused; + if (!WindowFocused) + CGame::InitAfterFocusLoss(); UpdateWindowMinimizedPause(); } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index db57a43..ec867f7 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -94,6 +94,7 @@ static psGlobalType PsGlobal; #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" +#include "CutsceneMgr.h" #include "PCSave.h" #include "AnimViewer.h" #include "MemoryMgr.h" @@ -140,13 +141,27 @@ IsFullscreenLikeWindowMode(void) #endif } +static RwBool +IsFocusLossInCutscene(void) +{ + return CGame::playingIntro || CCutsceneMgr::IsRunning() || CCutsceneMgr::IsCutsceneProcessing(); +} + static void UpdateWindowMinimizedPause(void) { RwBool pause = WindowMinimized || (IsFullscreenLikeWindowMode() && !WindowActive); + if (pause && IsFocusLossInCutscene()) { + CTimer::SetWindowMinimizedPause(false); + return; + } + + bool wasPaused = CTimer::GetIsWindowMinimizedPaused(); CTimer::SetWindowMinimizedPause(pause); if (pause) { + if (!wasPaused) + CGame::InitAfterFocusLoss(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); } @@ -287,12 +302,17 @@ psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { - ForegroundApp = FALSE; - WindowActive = FALSE; - UpdateWindowMinimizedPause(); - SetWindowAudioSuspended(TRUE); + if (!IsFocusLossInCutscene()) { + ForegroundApp = FALSE; + WindowActive = FALSE; + UpdateWindowMinimizedPause(); + SetWindowAudioSuspended(TRUE); + } return FALSE; } + + if ((WindowMinimized || (IsFullscreenLikeWindowMode() && !WindowActive)) && !IsFocusLossInCutscene()) + UpdateWindowMinimizedPause(); return TRUE; } From c19c7da2c914caa4847c494cedfc33695a80edfa Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 00:30:24 +0300 Subject: [PATCH 02/28] full screen... --- src/core/Game.cpp | 5 ++++- src/skel/glfw/glfw.cpp | 17 +++++------------ src/skel/win/win.cpp | 25 ++++++++++++------------- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 5879b2f..4f7312d 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -69,6 +69,7 @@ #include "Streaming.h" #include "SurfaceTable.h" #include "TempColModels.h" +#include "Timer.h" #include "Timecycle.h" #include "TrafficLights.h" #include "Train.h" @@ -1261,8 +1262,10 @@ CGame::InitAfterFocusLoss() FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = FrontEndMenuManager.m_lastWorking3DAudioProvider; DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_lastWorking3DAudioProvider); - if (!FrontEndMenuManager.m_bGameNotLoaded && !FrontEndMenuManager.m_bMenuActive) + if (!FrontEndMenuManager.m_bGameNotLoaded && !FrontEndMenuManager.m_bMenuActive) { + CTimer::StartUserPause(); FrontEndMenuManager.m_bStartUpFrontEndRequested = true; + } } bool diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index da9bd81..03dab9a 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -150,16 +150,14 @@ static void UpdateWindowMinimizedPause(void) { RwBool pause = IsWindowMinimizedPauseActive(); - if (pause && IsFocusLossInCutscene()) { - CTimer::SetWindowMinimizedPause(false); + CTimer::SetWindowMinimizedPause(false); + + if (pause && IsFocusLossInCutscene()) return; - } - bool wasPaused = CTimer::GetIsWindowMinimizedPaused(); - CTimer::SetWindowMinimizedPause(pause); if (pause) { - if (!wasPaused) + if (ForegroundApp) CGame::InitAfterFocusLoss(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); @@ -264,16 +262,11 @@ psCameraBeginUpdate(RwCamera *camera) if ( !RwCameraBeginUpdate(Scene.camera) ) { if (!IsFocusLossInCutscene()) { - ForegroundApp = FALSE; RefreshWindowActivityState(); - SetWindowAudioSuspended(TRUE); } return FALSE; } - if (IsWindowMinimizedPauseActive() && !IsFocusLossInCutscene()) - UpdateWindowMinimizedPause(); - return TRUE; } @@ -2411,7 +2404,7 @@ main(int argc, char *argv[]) { RwCameraEndUpdate(Scene.camera); RefreshWindowActivityState(); - if (!CTimer::GetIsWindowMinimizedPaused()) + if (!IsWindowMinimizedPauseActive()) { ForegroundApp = TRUE; SetWindowAudioSuspended(FALSE); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index ec867f7..b6cd613 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -141,6 +141,12 @@ IsFullscreenLikeWindowMode(void) #endif } +static RwBool +IsWindowMinimizedPauseActive(void) +{ + return WindowMinimized || (IsFullscreenLikeWindowMode() && !WindowActive); +} + static RwBool IsFocusLossInCutscene(void) { @@ -150,17 +156,15 @@ IsFocusLossInCutscene(void) static void UpdateWindowMinimizedPause(void) { - RwBool pause = WindowMinimized || (IsFullscreenLikeWindowMode() && !WindowActive); - if (pause && IsFocusLossInCutscene()) { - CTimer::SetWindowMinimizedPause(false); + RwBool pause = IsWindowMinimizedPauseActive(); + CTimer::SetWindowMinimizedPause(false); + + if (pause && IsFocusLossInCutscene()) return; - } - bool wasPaused = CTimer::GetIsWindowMinimizedPaused(); - CTimer::SetWindowMinimizedPause(pause); if (pause) { - if (!wasPaused) + if (ForegroundApp) CGame::InitAfterFocusLoss(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); @@ -303,16 +307,11 @@ psCameraBeginUpdate(RwCamera *camera) if ( !RwCameraBeginUpdate(Scene.camera) ) { if (!IsFocusLossInCutscene()) { - ForegroundApp = FALSE; WindowActive = FALSE; UpdateWindowMinimizedPause(); - SetWindowAudioSuspended(TRUE); } return FALSE; } - - if ((WindowMinimized || (IsFullscreenLikeWindowMode() && !WindowActive)) && !IsFocusLossInCutscene()) - UpdateWindowMinimizedPause(); return TRUE; } @@ -2639,7 +2638,7 @@ WinMain(HINSTANCE instance, { RwCameraEndUpdate(Scene.camera); RefreshWindowActivityState(); - if (!CTimer::GetIsWindowMinimizedPaused()) + if (!IsWindowMinimizedPauseActive()) { ForegroundApp = TRUE; SetWindowAudioSuspended(FALSE); From be4396edc30471b4899bc36188e50e049591cffe Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 00:44:14 +0300 Subject: [PATCH 03/28] ok --- src/core/Frontend.cpp | 3 ++- src/skel/crossplatform.h | 1 + src/skel/glfw/glfw.cpp | 17 +++++++++++++++-- src/skel/win/win.cpp | 18 ++++++++++++++++-- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index f30811b..cc89d21 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -5502,6 +5502,7 @@ CMenuManager::SwitchMenuOnAndOff() CPad::StopPadsShaking(); #endif } else { + psRestoreFocusAfterPause(); #ifdef EXTENDED_COLOURFILTER // we always expect CPostFX to be open CMBlur::BlurOn = true; @@ -5514,7 +5515,7 @@ CMenuManager::SwitchMenuOnAndOff() DoRWStuffEndOfFrame(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); - if (_InputMouseNeedsExclusive()) { + if (IsForegroundApp() && _InputMouseNeedsExclusive()) { _InputShutdownMouse(); _InputInitialiseMouse(true); } diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h index dbfd16f..a3c68ba 100644 --- a/src/skel/crossplatform.h +++ b/src/skel/crossplatform.h @@ -99,6 +99,7 @@ enum eGameState extern RwUInt32 gGameState; RwBool IsForegroundApp(); +void psRestoreFocusAfterPause(void); #ifndef MAX_PATH #if !defined _WIN32 || defined __MINGW32__ diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 03dab9a..561fd77 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -121,9 +121,9 @@ IsFocusLossInCutscene(void) } static void -SetWindowAudioSuspended(RwBool suspend) +SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { - if (WindowAudioSuspended == suspend) + if (!force && WindowAudioSuspended == suspend) return; WindowAudioSuspended = suspend; @@ -172,6 +172,19 @@ RefreshWindowActivityState(void) UpdateWindowMinimizedPause(); } +void +psRestoreFocusAfterPause(void) +{ + CTimer::SetWindowMinimizedPause(false); + WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); + WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); + + if (!IsWindowMinimizedPauseActive()) { + ForegroundApp = TRUE; + SetWindowAudioSuspended(FALSE, TRUE); + } +} + size_t _dwMemAvailPhys; RwUInt32 gGameState; diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index b6cd613..7c101d8 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -107,9 +107,9 @@ ReleaseWindowMouseFocus(void) } static void -SetWindowAudioSuspended(RwBool suspend) +SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { - if (WindowAudioSuspended == suspend) + if (!force && WindowAudioSuspended == suspend) return; WindowAudioSuspended = suspend; @@ -180,6 +180,20 @@ RefreshWindowActivityState(void) UpdateWindowMinimizedPause(); } +void +psRestoreFocusAfterPause(void) +{ + HWND currentWindow = PSGLOBAL(window); + CTimer::SetWindowMinimizedPause(false); + WindowMinimized = IsIconic(currentWindow); + WindowActive = GetForegroundWindow() == currentWindow || GetActiveWindow() == currentWindow; + + if (!IsWindowMinimizedPauseActive()) { + ForegroundApp = TRUE; + SetWindowAudioSuspended(FALSE, TRUE); + } +} + #ifdef PS2_MENU #include "MemoryCard.h" #include "Font.h" From 0a76d7dbd3d537efeac09d3100b7b69e8a03a5a8 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 01:01:10 +0300 Subject: [PATCH 04/28] fix cutscenes --- src/core/Game.cpp | 15 ++++++++++++++- src/core/Game.h | 1 + src/skel/glfw/glfw.cpp | 27 +++++++++++++++++---------- src/skel/win/win.cpp | 32 +++++++++++++++++++++----------- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 4f7312d..4168a35 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -9,6 +9,7 @@ #include "Bridge.h" #include "CarCtrl.h" #include "CarGen.h" +#include "Camera.h" #include "CdStream.h" #include "Clock.h" #include "Clouds.h" @@ -1256,7 +1257,7 @@ void CGame::ProcessTidyUpMemory(void) void CGame::InitAfterFocusLoss() { - if (CGame::playingIntro || CCutsceneMgr::IsRunning() || CCutsceneMgr::IsCutsceneProcessing()) + if (ShouldUseBackgroundPauseAfterFocusLoss()) return; FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = FrontEndMenuManager.m_lastWorking3DAudioProvider; @@ -1268,6 +1269,18 @@ CGame::InitAfterFocusLoss() } } +bool +CGame::ShouldUseBackgroundPauseAfterFocusLoss() +{ + return CGame::playingIntro + || CCutsceneMgr::IsRunning() + || CCutsceneMgr::IsCutsceneProcessing() + || TheCamera.m_WideScreenOn + || CTheScripts::UseTextCommands != 0 + || CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_CUTSCENE) + || CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PHONE); +} + bool CGame::CanSeeWaterFromCurrArea(void) { diff --git a/src/core/Game.h b/src/core/Game.h index 9c4bdf1..8472a86 100644 --- a/src/core/Game.h +++ b/src/core/Game.h @@ -68,6 +68,7 @@ class CGame static void Process(void); static void InitAfterFocusLoss(void); + static bool ShouldUseBackgroundPauseAfterFocusLoss(void); static bool IsInInterior(void) { return currArea != AREA_MAIN_MAP; } static bool CanSeeWaterFromCurrArea(void); diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 561fd77..8bc3fd3 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -40,6 +40,7 @@ long _dwOperatingSystemVersion; #include "Pad.h" #include "Timer.h" #include "DMAudio.h" +#include "sampman.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" @@ -115,9 +116,16 @@ IsWindowMinimizedPauseActive(void) } static RwBool -IsFocusLossInCutscene(void) +ShouldUseBackgroundPauseAfterFocusLoss(void) { - return CGame::playingIntro || CCutsceneMgr::IsRunning() || CCutsceneMgr::IsCutsceneProcessing(); + return CGame::ShouldUseBackgroundPauseAfterFocusLoss(); +} + +static void +SetWindowStreamsPaused(RwBool pause) +{ + for (int32 i = 0; i < MAX_STREAMS; i++) + SampleManager.PauseStream(pause, i); } static void @@ -130,6 +138,7 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (suspend) { _InputShutdownMouse(); + SetWindowStreamsPaused(TRUE); DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); @@ -141,6 +150,8 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (PSGLOBAL(fullScreen)) RsEventHandler(rsACTIVATE, (void *)TRUE); _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); + if (!CTimer::GetIsUserPaused() && !CTimer::GetIsWindowMinimizedPaused()) + SetWindowStreamsPaused(FALSE); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); } @@ -150,14 +161,12 @@ static void UpdateWindowMinimizedPause(void) { RwBool pause = IsWindowMinimizedPauseActive(); - CTimer::SetWindowMinimizedPause(false); - - if (pause && IsFocusLossInCutscene()) - return; + RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); + CTimer::SetWindowMinimizedPause(backgroundPause); if (pause) { - if (ForegroundApp) + if (ForegroundApp && !backgroundPause) CGame::InitAfterFocusLoss(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); @@ -274,9 +283,7 @@ psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { - if (!IsFocusLossInCutscene()) { - RefreshWindowActivityState(); - } + RefreshWindowActivityState(); return FALSE; } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 7c101d8..33d312c 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -91,6 +91,7 @@ static psGlobalType PsGlobal; #include "Pad.h" #include "Timer.h" #include "DMAudio.h" +#include "sampman.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" @@ -102,10 +103,19 @@ static psGlobalType PsGlobal; static void ReleaseWindowMouseFocus(void) { + ReleaseCapture(); + ClipCursor(nil); if (PSGLOBAL(mouse) != nil) PSGLOBAL(mouse)->Unacquire(); } +static void +SetWindowStreamsPaused(RwBool pause) +{ + for (int32 i = 0; i < MAX_STREAMS; i++) + SampleManager.PauseStream(pause, i); +} + static void SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { @@ -116,6 +126,7 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (suspend) { ReleaseWindowMouseFocus(); + SetWindowStreamsPaused(TRUE); DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); @@ -126,6 +137,8 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { if (PSGLOBAL(fullScreen)) RsEventHandler(rsACTIVATE, (void *)TRUE); + if (!CTimer::GetIsUserPaused() && !CTimer::GetIsWindowMinimizedPaused()) + SetWindowStreamsPaused(FALSE); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); } @@ -148,23 +161,21 @@ IsWindowMinimizedPauseActive(void) } static RwBool -IsFocusLossInCutscene(void) +ShouldUseBackgroundPauseAfterFocusLoss(void) { - return CGame::playingIntro || CCutsceneMgr::IsRunning() || CCutsceneMgr::IsCutsceneProcessing(); + return CGame::ShouldUseBackgroundPauseAfterFocusLoss(); } static void UpdateWindowMinimizedPause(void) { RwBool pause = IsWindowMinimizedPauseActive(); - CTimer::SetWindowMinimizedPause(false); - - if (pause && IsFocusLossInCutscene()) - return; + RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); + CTimer::SetWindowMinimizedPause(backgroundPause); if (pause) { - if (ForegroundApp) + if (ForegroundApp && !backgroundPause) CGame::InitAfterFocusLoss(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); @@ -320,10 +331,9 @@ psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { - if (!IsFocusLossInCutscene()) { - WindowActive = FALSE; - UpdateWindowMinimizedPause(); - } + WindowActive = FALSE; + WindowMinimized = IsIconic(PSGLOBAL(window)); + UpdateWindowMinimizedPause(); return FALSE; } From 177ab6b2d79786ed292a793140ecf20066cb7f36 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 01:14:02 +0300 Subject: [PATCH 05/28] yes --- src/audio/MusicManager.cpp | 10 +++++++++- src/skel/glfw/glfw.cpp | 29 ++++++++++++----------------- src/skel/win/win.cpp | 29 ++++++++++++----------------- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/audio/MusicManager.cpp b/src/audio/MusicManager.cpp index f2e3cfd..da7cf4f 100644 --- a/src/audio/MusicManager.cpp +++ b/src/audio/MusicManager.cpp @@ -369,7 +369,15 @@ cMusicManager::Service() { case MUSICMODE_FRONTEND: ServiceFrontEndMode(); break; case MUSICMODE_GAME: ServiceGameMode(); break; - case MUSICMODE_CUTSCENE: SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 1, 0); break; + case MUSICMODE_CUTSCENE: + if (AudioManager.m_nUserPause) + SampleManager.PauseStream(1, 0); + else { + if (AudioManager.m_nPreviousUserPause) + SampleManager.PauseStream(0, 0); + SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 1, 0); + } + break; } } else diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 8bc3fd3..f3e3312 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -40,7 +40,6 @@ long _dwOperatingSystemVersion; #include "Pad.h" #include "Timer.h" #include "DMAudio.h" -#include "sampman.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" @@ -121,13 +120,6 @@ ShouldUseBackgroundPauseAfterFocusLoss(void) return CGame::ShouldUseBackgroundPauseAfterFocusLoss(); } -static void -SetWindowStreamsPaused(RwBool pause) -{ - for (int32 i = 0; i < MAX_STREAMS; i++) - SampleManager.PauseStream(pause, i); -} - static void SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { @@ -138,7 +130,6 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (suspend) { _InputShutdownMouse(); - SetWindowStreamsPaused(TRUE); DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); @@ -150,10 +141,9 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (PSGLOBAL(fullScreen)) RsEventHandler(rsACTIVATE, (void *)TRUE); _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); - if (!CTimer::GetIsUserPaused() && !CTimer::GetIsWindowMinimizedPaused()) - SetWindowStreamsPaused(FALSE); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + DMAudio.Service(); } } @@ -164,13 +154,18 @@ UpdateWindowMinimizedPause(void) RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); CTimer::SetWindowMinimizedPause(backgroundPause); - if (pause) - { - if (ForegroundApp && !backgroundPause) - CGame::InitAfterFocusLoss(); - ForegroundApp = FALSE; - SetWindowAudioSuspended(TRUE); + if (!pause) { + if (WindowAudioSuspended) { + ForegroundApp = TRUE; + SetWindowAudioSuspended(FALSE, TRUE); + } + return; } + + if (ForegroundApp && !backgroundPause) + CGame::InitAfterFocusLoss(); + ForegroundApp = FALSE; + SetWindowAudioSuspended(TRUE); } static void diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 33d312c..9f1c8ed 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -91,7 +91,6 @@ static psGlobalType PsGlobal; #include "Pad.h" #include "Timer.h" #include "DMAudio.h" -#include "sampman.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" @@ -109,13 +108,6 @@ ReleaseWindowMouseFocus(void) PSGLOBAL(mouse)->Unacquire(); } -static void -SetWindowStreamsPaused(RwBool pause) -{ - for (int32 i = 0; i < MAX_STREAMS; i++) - SampleManager.PauseStream(pause, i); -} - static void SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { @@ -126,7 +118,6 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (suspend) { ReleaseWindowMouseFocus(); - SetWindowStreamsPaused(TRUE); DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); @@ -137,10 +128,9 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { if (PSGLOBAL(fullScreen)) RsEventHandler(rsACTIVATE, (void *)TRUE); - if (!CTimer::GetIsUserPaused() && !CTimer::GetIsWindowMinimizedPaused()) - SetWindowStreamsPaused(FALSE); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + DMAudio.Service(); } } @@ -173,13 +163,18 @@ UpdateWindowMinimizedPause(void) RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); CTimer::SetWindowMinimizedPause(backgroundPause); - if (pause) - { - if (ForegroundApp && !backgroundPause) - CGame::InitAfterFocusLoss(); - ForegroundApp = FALSE; - SetWindowAudioSuspended(TRUE); + if (!pause) { + if (WindowAudioSuspended) { + ForegroundApp = TRUE; + SetWindowAudioSuspended(FALSE, TRUE); + } + return; } + + if (ForegroundApp && !backgroundPause) + CGame::InitAfterFocusLoss(); + ForegroundApp = FALSE; + SetWindowAudioSuspended(TRUE); } static void From 945a5772c9f67dbc07d21dbbad80215eb3c47619 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 01:33:40 +0300 Subject: [PATCH 06/28] . --- src/audio/AudioLogic.cpp | 5 +++-- src/skel/glfw/glfw.cpp | 18 +++++++++--------- src/skel/win/win.cpp | 18 +++++++++--------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/audio/AudioLogic.cpp b/src/audio/AudioLogic.cpp index 87521f0..b889f80 100644 --- a/src/audio/AudioLogic.cpp +++ b/src/audio/AudioLogic.cpp @@ -9872,11 +9872,12 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) } switch (m_sMissionAudio.m_nPlayStatus[slot]) { case PLAY_STATUS_STOPPED: + if (m_nUserPause) + break; + if (MissionScriptAudioUsesPoliceChannel(m_sMissionAudio.m_nSampleIndex[slot])) { SetMissionScriptPoliceAudio(m_sMissionAudio.m_nSampleIndex[slot]); } else { - if (m_nUserPause) - SampleManager.PauseStream(1, slot + 1); if (m_sMissionAudio.m_bPredefinedProperties[slot]) { if (m_sMissionAudio.m_nSampleIndex[slot] == STREAMED_SOUND_MISSION_CAMERAL) SampleManager.SetStreamedVolumeAndPan(80, 0, 1, slot + 1); diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index f3e3312..d882dfb 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -155,10 +155,9 @@ UpdateWindowMinimizedPause(void) CTimer::SetWindowMinimizedPause(backgroundPause); if (!pause) { - if (WindowAudioSuspended) { - ForegroundApp = TRUE; + ForegroundApp = TRUE; + if (WindowAudioSuspended) SetWindowAudioSuspended(FALSE, TRUE); - } return; } @@ -2415,15 +2414,16 @@ main(int argc, char *argv[]) } else { + RefreshWindowActivityState(); + if (!IsWindowMinimizedPauseActive()) + { + ForegroundApp = TRUE; + SetWindowAudioSuspended(FALSE); + } + if ( RwCameraBeginUpdate(Scene.camera) ) { RwCameraEndUpdate(Scene.camera); - RefreshWindowActivityState(); - if (!IsWindowMinimizedPauseActive()) - { - ForegroundApp = TRUE; - SetWindowAudioSuspended(FALSE); - } } } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 9f1c8ed..cac886b 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -164,10 +164,9 @@ UpdateWindowMinimizedPause(void) CTimer::SetWindowMinimizedPause(backgroundPause); if (!pause) { - if (WindowAudioSuspended) { - ForegroundApp = TRUE; + ForegroundApp = TRUE; + if (WindowAudioSuspended) SetWindowAudioSuspended(FALSE, TRUE); - } return; } @@ -2653,15 +2652,16 @@ WinMain(HINSTANCE instance, } else { + RefreshWindowActivityState(); + if (!IsWindowMinimizedPauseActive()) + { + ForegroundApp = TRUE; + SetWindowAudioSuspended(FALSE); + } + if ( RwCameraBeginUpdate(Scene.camera) ) { RwCameraEndUpdate(Scene.camera); - RefreshWindowActivityState(); - if (!IsWindowMinimizedPauseActive()) - { - ForegroundApp = TRUE; - SetWindowAudioSuspended(FALSE); - } } WaitMessage(); From 6924fca43836cf86c3bf4587e137b7ac97e00793 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 01:48:49 +0300 Subject: [PATCH 07/28] final? --- src/animation/CutsceneMgr.cpp | 6 +++++- src/audio/AudioLogic.cpp | 38 +++++++++++++++++------------------ src/core/main.cpp | 3 +++ src/skel/crossplatform.h | 1 + src/skel/glfw/glfw.cpp | 20 ++++++++++++------ src/skel/win/win.cpp | 23 ++++++++++++++------- 6 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp index 633618b..fd74e23 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/animation/CutsceneMgr.cpp @@ -22,6 +22,7 @@ #include "ColStore.h" #include "Radar.h" #include "Pools.h" +#include "Timer.h" const struct { const char *szTrackName; @@ -506,6 +507,9 @@ CCutsceneMgr::Update(void) CUTSCENE_LOADING_4 }; + if (CTimer::GetIsWindowMinimizedPaused()) + return; + switch (ms_cutsceneLoadStatus) { case CUTSCENE_LOADING_AUDIO: SetupCutsceneToStart(); @@ -680,4 +684,4 @@ CCutsceneMgr::RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver() pPlayerPed->ClearWeapons(); CGame::DrasticTidyUpMemory(true); -} \ No newline at end of file +} diff --git a/src/audio/AudioLogic.cpp b/src/audio/AudioLogic.cpp index b889f80..54e74b4 100644 --- a/src/audio/AudioLogic.cpp +++ b/src/audio/AudioLogic.cpp @@ -9925,29 +9925,27 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) m_sMissionAudio.m_nMissionAudioCounter[slot] = 0; } } + } else if (m_nUserPause) { + SampleManager.PauseStream(1, slot + 1); } else if (m_sMissionAudio.m_bIsPlaying[slot]) { + if (m_nPreviousUserPause) + SampleManager.PauseStream(0, slot + 1); if (SampleManager.IsStreamPlaying(slot + 1) || m_nUserPause || m_nPreviousUserPause) { - if (m_nUserPause) - SampleManager.PauseStream(1, slot + 1); - else - { - SampleManager.PauseStream(0, slot + 1); - if (!m_sMissionAudio.m_bPredefinedProperties[slot]) { - distSquared = GetDistanceSquared(m_sMissionAudio.m_vecPos[slot]); - if (distSquared >= SQR(80.0f)) { - emittingVol = 0; - pan = 63; - } else { - emittingVol = 127; - if (distSquared > 0.0f) { - dist = Sqrt(distSquared); - emittingVol = ComputeVolume(127, 80.0f, dist); - } - TranslateEntity(&m_sMissionAudio.m_vecPos[slot], &vec); - pan = ComputePan(80.f, &vec); + if (!m_sMissionAudio.m_bPredefinedProperties[slot]) { + distSquared = GetDistanceSquared(m_sMissionAudio.m_vecPos[slot]); + if (distSquared >= SQR(80.0f)) { + emittingVol = 0; + pan = 63; + } else { + emittingVol = 127; + if (distSquared > 0.0f) { + dist = Sqrt(distSquared); + emittingVol = ComputeVolume(127, 80.0f, dist); } - SampleManager.SetStreamedVolumeAndPan(emittingVol, pan, 1, slot + 1); + TranslateEntity(&m_sMissionAudio.m_vecPos[slot], &vec); + pan = ComputePan(80.f, &vec); } + SampleManager.SetStreamedVolumeAndPan(emittingVol, pan, 1, slot + 1); } } else if (m_sMissionAudio.m_nSampleIndex[slot] == STREAMED_SOUND_MISSION_ROK2_01) { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_STOPPED; @@ -9962,6 +9960,8 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) } else { if (m_nUserPause) break; + if (m_nPreviousUserPause) + SampleManager.PauseStream(0, slot + 1); if (nCheckPlayingDelay[slot]--) { if (!SampleManager.IsStreamPlaying(slot + 1)) break; diff --git a/src/core/main.cpp b/src/core/main.cpp index ab4a4d0..e1028d5 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -10,6 +10,7 @@ #endif #include "main.h" +#include "crossplatform.h" #include "CdStream.h" #include "General.h" #include "RwHelper.h" @@ -1452,6 +1453,7 @@ Render2dStuffAfterFade(void) void Idle(void *arg) { + psRefreshFocusAfterPause(); CTimer::Update(); tbInit(); @@ -1620,6 +1622,7 @@ void FrontendIdle(void) { CDraw::CalculateAspectRatio(); + psRefreshFocusAfterPause(); CTimer::Update(); CSprite2d::SetRecipNearClip(); // this should be on InitialiseRenderWare according to PS2 asm. seems like a bug fix CSprite2d::InitPerFrame(); diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h index a3c68ba..b6674dc 100644 --- a/src/skel/crossplatform.h +++ b/src/skel/crossplatform.h @@ -99,6 +99,7 @@ enum eGameState extern RwUInt32 gGameState; RwBool IsForegroundApp(); +void psRefreshFocusAfterPause(void); void psRestoreFocusAfterPause(void); #ifndef MAX_PATH diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index d882dfb..ca3ac9e 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -152,11 +152,12 @@ UpdateWindowMinimizedPause(void) { RwBool pause = IsWindowMinimizedPauseActive(); RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); + RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); CTimer::SetWindowMinimizedPause(backgroundPause); if (!pause) { ForegroundApp = TRUE; - if (WindowAudioSuspended) + if (WindowAudioSuspended || wasWindowPause) SetWindowAudioSuspended(FALSE, TRUE); return; } @@ -175,6 +176,12 @@ RefreshWindowActivityState(void) UpdateWindowMinimizedPause(); } +void +psRefreshFocusAfterPause(void) +{ + RefreshWindowActivityState(); +} + void psRestoreFocusAfterPause(void) { @@ -2418,14 +2425,15 @@ main(int argc, char *argv[]) if (!IsWindowMinimizedPauseActive()) { ForegroundApp = TRUE; - SetWindowAudioSuspended(FALSE); + SetWindowAudioSuspended(FALSE, TRUE); } - - if ( RwCameraBeginUpdate(Scene.camera) ) + else { - RwCameraEndUpdate(Scene.camera); + if ( RwCameraBeginUpdate(Scene.camera) ) + { + RwCameraEndUpdate(Scene.camera); + } } - } } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index cac886b..1763bcd 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -161,11 +161,12 @@ UpdateWindowMinimizedPause(void) { RwBool pause = IsWindowMinimizedPauseActive(); RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); + RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); CTimer::SetWindowMinimizedPause(backgroundPause); if (!pause) { ForegroundApp = TRUE; - if (WindowAudioSuspended) + if (WindowAudioSuspended || wasWindowPause) SetWindowAudioSuspended(FALSE, TRUE); return; } @@ -185,6 +186,12 @@ RefreshWindowActivityState(void) UpdateWindowMinimizedPause(); } +void +psRefreshFocusAfterPause(void) +{ + RefreshWindowActivityState(); +} + void psRestoreFocusAfterPause(void) { @@ -2656,15 +2663,17 @@ WinMain(HINSTANCE instance, if (!IsWindowMinimizedPauseActive()) { ForegroundApp = TRUE; - SetWindowAudioSuspended(FALSE); + SetWindowAudioSuspended(FALSE, TRUE); } - - if ( RwCameraBeginUpdate(Scene.camera) ) + else { - RwCameraEndUpdate(Scene.camera); + if ( RwCameraBeginUpdate(Scene.camera) ) + { + RwCameraEndUpdate(Scene.camera); + } + + WaitMessage(); } - - WaitMessage(); } } From 246044a9e89d5fcd8a1a6e92360f8815d4305478 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 02:04:56 +0300 Subject: [PATCH 08/28] ... --- src/audio/AudioLogic.cpp | 35 +++++++++++++++++++++++++++++++---- src/skel/glfw/glfw.cpp | 4 ---- src/skel/win/win.cpp | 4 ---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/audio/AudioLogic.cpp b/src/audio/AudioLogic.cpp index 54e74b4..0694f14 100644 --- a/src/audio/AudioLogic.cpp +++ b/src/audio/AudioLogic.cpp @@ -9841,8 +9841,16 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) static uint8 nCheckPlayingDelay[MISSION_AUDIO_SLOTS] = { 0, 0 }; static uint8 nFramesUntilFailedLoad[MISSION_AUDIO_SLOTS] = { 0, 0 }; static uint8 nFramesForPretendPlaying[MISSION_AUDIO_SLOTS] = { 0, 0 }; + static uint8 nFramesUntilFailedResume[MISSION_AUDIO_SLOTS] = { 0, 0 }; + const uint8 RESUME_RETRY_FRAMES = 60; - if (m_sMissionAudio.m_nSampleIndex[slot] == NO_SAMPLE) return; + if (m_sMissionAudio.m_nSampleIndex[slot] == NO_SAMPLE) { + nCheckPlayingDelay[slot] = 0; + nFramesUntilFailedLoad[slot] = 0; + nFramesForPretendPlaying[slot] = 0; + nFramesUntilFailedResume[slot] = 0; + return; + } switch (m_sMissionAudio.m_nLoadingStatus[slot]) { case LOADING_STATUS_NOT_LOADED: @@ -9860,6 +9868,7 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) nFramesForPretendPlaying[slot] = 0; nCheckPlayingDelay[slot] = 0; nFramesUntilFailedLoad[slot] = 0; + nFramesUntilFailedResume[slot] = 0; } else if (!m_nUserPause) { if (++nFramesForPretendPlaying[slot] < 90) { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_PLAYING; @@ -9905,6 +9914,7 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) } m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_PLAYING; nCheckPlayingDelay[slot] = 30; + nFramesUntilFailedResume[slot] = 0; if (m_sMissionAudio.m_nSampleIndex[slot] >= STREAMED_SOUND_MISSION_MOB_01A && m_sMissionAudio.m_nSampleIndex[slot] <= STREAMED_SOUND_MISSION_MOB_99A) m_sMissionAudio.m_bIsMobile[slot] = true; break; @@ -9912,6 +9922,7 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) if (m_bTimerJustReset) { ClearMissionAudio(slot); SampleManager.StopStreamedFile(slot + 1); + nFramesUntilFailedResume[slot] = 0; break; } if (MissionScriptAudioUsesPoliceChannel(m_sMissionAudio.m_nSampleIndex[slot])) { @@ -9927,10 +9938,15 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) } } else if (m_nUserPause) { SampleManager.PauseStream(1, slot + 1); + nFramesUntilFailedResume[slot] = 0; } else if (m_sMissionAudio.m_bIsPlaying[slot]) { - if (m_nPreviousUserPause) + if (m_nPreviousUserPause) { SampleManager.PauseStream(0, slot + 1); + nFramesUntilFailedResume[slot] = RESUME_RETRY_FRAMES; + } if (SampleManager.IsStreamPlaying(slot + 1) || m_nUserPause || m_nPreviousUserPause) { + if (SampleManager.IsStreamPlaying(slot + 1)) + nFramesUntilFailedResume[slot] = 0; if (!m_sMissionAudio.m_bPredefinedProperties[slot]) { distSquared = GetDistanceSquared(m_sMissionAudio.m_vecPos[slot]); if (distSquared >= SQR(80.0f)) { @@ -9947,6 +9963,10 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) } SampleManager.SetStreamedVolumeAndPan(emittingVol, pan, 1, slot + 1); } + } else if (nFramesUntilFailedResume[slot]) { + SampleManager.PauseStream(0, slot + 1); + SampleManager.StartPreloadedStreamedFile(slot + 1); + --nFramesUntilFailedResume[slot]; } else if (m_sMissionAudio.m_nSampleIndex[slot] == STREAMED_SOUND_MISSION_ROK2_01) { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_STOPPED; } else { @@ -9956,17 +9976,24 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) m_sMissionAudio.m_nSampleIndex[slot] = NO_SAMPLE; SampleManager.StopStreamedFile(slot + 1); m_sMissionAudio.m_nMissionAudioCounter[slot] = 0; + nFramesUntilFailedResume[slot] = 0; } } else { if (m_nUserPause) break; - if (m_nPreviousUserPause) + if (m_nPreviousUserPause) { SampleManager.PauseStream(0, slot + 1); - if (nCheckPlayingDelay[slot]--) { + SampleManager.StartPreloadedStreamedFile(slot + 1); + nFramesUntilFailedResume[slot] = RESUME_RETRY_FRAMES; + } + if (nCheckPlayingDelay[slot]) { + --nCheckPlayingDelay[slot]; if (!SampleManager.IsStreamPlaying(slot + 1)) break; nCheckPlayingDelay[slot] = 0; } + if (SampleManager.IsStreamPlaying(slot + 1)) + nFramesUntilFailedResume[slot] = 0; m_sMissionAudio.m_bIsPlaying[slot] = true; } break; diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index ca3ac9e..2450bea 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -133,13 +133,9 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); - if (PSGLOBAL(fullScreen)) - RsEventHandler(rsACTIVATE, (void *)FALSE); } else { - if (PSGLOBAL(fullScreen)) - RsEventHandler(rsACTIVATE, (void *)TRUE); _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 1763bcd..15c1d5d 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -121,13 +121,9 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); - if (PSGLOBAL(fullScreen)) - RsEventHandler(rsACTIVATE, (void *)FALSE); } else { - if (PSGLOBAL(fullScreen)) - RsEventHandler(rsACTIVATE, (void *)TRUE); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); DMAudio.Service(); From 1b94f0bda44322e78150f8010087be9ce11fea96 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 02:30:46 +0300 Subject: [PATCH 09/28] final bug fix --- src/animation/CutsceneMgr.cpp | 50 ++++++++++++++++++++++++++++++++--- src/animation/CutsceneMgr.h | 5 ++++ src/audio/DMAudio.cpp | 14 +++++++++- src/audio/DMAudio.h | 2 ++ src/audio/MusicManager.cpp | 22 +++++++++++++++ src/audio/MusicManager.h | 2 ++ src/skel/glfw/glfw.cpp | 2 ++ src/skel/win/win.cpp | 2 ++ 8 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp index fd74e23..46e207d 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/animation/CutsceneMgr.cpp @@ -130,6 +130,12 @@ float CCutsceneMgr::ms_cutsceneTimer; bool CCutsceneMgr::ms_wasCutsceneSkipped; uint32 CCutsceneMgr::ms_cutsceneLoadStatus; bool CCutsceneMgr::ms_useCutsceneShadows = true; +bool CCutsceneMgr::ms_cutsceneHasMusic; +bool CCutsceneMgr::ms_cutsceneMusicNeedsResume; +bool CCutsceneMgr::ms_wasWindowPaused; +uint32 CCutsceneMgr::ms_cutsceneMusicResumeFrames; + +static const uint32 CUTSCENE_MUSIC_RESUME_RETRY_FRAMES = 60; bool bCamLoaded; bool bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver; // pls don't shrink the name :P @@ -165,6 +171,10 @@ CCutsceneMgr::Initialise(void) ms_useLodMultiplier = false; ms_animLoaded = false; ms_cutsceneProcessing = false; + ms_cutsceneHasMusic = false; + ms_cutsceneMusicNeedsResume = false; + ms_wasWindowPaused = false; + ms_cutsceneMusicResumeFrames = 0; ms_pCutsceneDir = new CDirectory(CUTSCENEDIRSIZE); ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.DIR"); @@ -189,6 +199,10 @@ CCutsceneMgr::LoadCutsceneData(const char *szCutsceneName) ms_cutsceneProcessing = true; ms_wasCutsceneSkipped = false; + ms_cutsceneHasMusic = false; + ms_cutsceneMusicNeedsResume = false; + ms_wasWindowPaused = false; + ms_cutsceneMusicResumeFrames = 0; CTimer::Suspend(); if (!bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver) CStreaming::RemoveCurrentZonesModels(); @@ -242,6 +256,7 @@ CCutsceneMgr::LoadCutsceneData(const char *szCutsceneName) printf("Start preload audio %s\n", szCutsceneName); DMAudio.PreloadCutSceneMusic(trackId); printf("End preload audio %s\n", szCutsceneName); + ms_cutsceneHasMusic = true; } } @@ -272,6 +287,17 @@ CCutsceneMgr::FinishCutscene() CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(false); } +void +CCutsceneMgr::RecordWindowPause(void) +{ + if (ms_loaded || ms_running || ms_cutsceneProcessing || ms_cutsceneLoadStatus != 0) { + ms_wasWindowPaused = true; + ms_cutsceneMusicNeedsResume = ms_cutsceneMusicNeedsResume || + (ms_cutsceneHasMusic && (ms_cutsceneLoadStatus != 0 || DMAudio.IsCutSceneMusicPlaying())); + ms_cutsceneMusicResumeFrames = CUTSCENE_MUSIC_RESUME_RETRY_FRAMES; + } +} + void CCutsceneMgr::SetupCutsceneToStart(void) { @@ -453,6 +479,10 @@ CCutsceneMgr::DeleteCutsceneData(void) } ms_running = false; ms_loaded = false; + ms_cutsceneHasMusic = false; + ms_cutsceneMusicNeedsResume = false; + ms_wasWindowPaused = false; + ms_cutsceneMusicResumeFrames = 0; FindPlayerPed()->bIsVisible = true; CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_CUTSCENE); @@ -507,13 +537,15 @@ CCutsceneMgr::Update(void) CUTSCENE_LOADING_4 }; - if (CTimer::GetIsWindowMinimizedPaused()) + if (CTimer::GetIsWindowMinimizedPaused()) { + RecordWindowPause(); return; + } switch (ms_cutsceneLoadStatus) { case CUTSCENE_LOADING_AUDIO: SetupCutsceneToStart(); - if (CGeneral::faststricmp(ms_cutsceneName, "finale")) + if (ms_cutsceneHasMusic) DMAudio.PlayPreloadedCutSceneMusic(); ms_cutsceneLoadStatus++; break; @@ -530,6 +562,18 @@ CCutsceneMgr::Update(void) if (!ms_running) return; + if (ms_wasWindowPaused) { + if (ms_cutsceneMusicNeedsResume && ms_cutsceneMusicResumeFrames != 0 && !DMAudio.ResumeCutSceneMusicAfterPause()) { + --ms_cutsceneMusicResumeFrames; + CTimer::SetTimeStep(0.0f); + CTimer::SetTimeStepNonClipped(0.0f); + return; + } + ms_wasWindowPaused = false; + ms_cutsceneMusicNeedsResume = false; + ms_cutsceneMusicResumeFrames = 0; + } + ms_cutsceneTimer += CTimer::GetTimeStepNonClippedInSeconds(); for (int i = 0; i < ms_numCutsceneObjs; i++) { @@ -552,7 +596,7 @@ CCutsceneMgr::Update(void) } } -bool CCutsceneMgr::HasCutsceneFinished(void) { return !bCamLoaded || TheCamera.GetPositionAlongSpline() == 1.0f; } +bool CCutsceneMgr::HasCutsceneFinished(void) { return !bCamLoaded || TheCamera.GetPositionAlongSpline() >= 1.0f; } void CCutsceneMgr::LoadAnimationUncompressed(char const* name) diff --git a/src/animation/CutsceneMgr.h b/src/animation/CutsceneMgr.h index 51ef6c0..12bb5ff 100644 --- a/src/animation/CutsceneMgr.h +++ b/src/animation/CutsceneMgr.h @@ -24,6 +24,10 @@ class CCutsceneMgr static bool ms_wasCutsceneSkipped; static bool ms_cutsceneProcessing; static bool ms_useCutsceneShadows; + static bool ms_cutsceneHasMusic; + static bool ms_cutsceneMusicNeedsResume; + static bool ms_wasWindowPaused; + static uint32 ms_cutsceneMusicResumeFrames; public: static CDirectory *ms_pCutsceneDir; static uint32 ms_cutsceneLoadStatus; @@ -44,6 +48,7 @@ class CCutsceneMgr static void Shutdown(void); static void LoadCutsceneData(const char *szCutsceneName); static void FinishCutscene(void); + static void RecordWindowPause(void); static void SetupCutsceneToStart(void); static void SetCutsceneAnim(const char *animName, CObject *pObject); static void SetCutsceneAnimToLoop(const char *animName); diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index 7c99e89..64cdf57 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -279,6 +279,18 @@ cDMAudio::PlayPreloadedCutSceneMusic(void) MusicManager.PlayPreloadedCutSceneMusic(); } +bool +cDMAudio::ResumeCutSceneMusicAfterPause(void) +{ + return MusicManager.ResumeCutSceneMusicAfterPause(); +} + +bool +cDMAudio::IsCutSceneMusicPlaying(void) +{ + return MusicManager.IsCutSceneMusicPlaying(); +} + void cDMAudio::StopCutSceneMusic(void) { @@ -379,4 +391,4 @@ void cDMAudio::ShutUpPlayerTalking(uint8 state) { AudioManager.m_bIsPlayerShutUp = state; -} \ No newline at end of file +} diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index 2c10043..e5042e5 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -79,6 +79,8 @@ class cDMAudio void PreloadCutSceneMusic(uint32 track); void PlayPreloadedCutSceneMusic(void); + bool ResumeCutSceneMusicAfterPause(void); + bool IsCutSceneMusicPlaying(void); void StopCutSceneMusic(void); void PreloadMissionAudio(uint8 slot, Const char *missionAudio); diff --git a/src/audio/MusicManager.cpp b/src/audio/MusicManager.cpp index da7cf4f..400fe6b 100644 --- a/src/audio/MusicManager.cpp +++ b/src/audio/MusicManager.cpp @@ -1094,6 +1094,28 @@ cMusicManager::PlayPreloadedCutSceneMusic(void) SampleManager.StartPreloadedStreamedFile(0); } +bool +cMusicManager::ResumeCutSceneMusicAfterPause(void) +{ + if (!IsInitialised() || m_bDisabled || m_nMusicMode != MUSICMODE_CUTSCENE || m_nPlayingTrack == NO_TRACK) + return true; + + SampleManager.PauseStream(0, 0); + SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 1, 0); + if (SampleManager.IsStreamPlaying(0)) + return true; + + SampleManager.StartPreloadedStreamedFile(0); + return SampleManager.IsStreamPlaying(0); +} + +bool +cMusicManager::IsCutSceneMusicPlaying(void) +{ + return IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE && + m_nPlayingTrack != NO_TRACK && SampleManager.IsStreamPlaying(0); +} + void cMusicManager::StopCutSceneMusic(void) { diff --git a/src/audio/MusicManager.h b/src/audio/MusicManager.h index 27456d7..2c5cdbf 100644 --- a/src/audio/MusicManager.h +++ b/src/audio/MusicManager.h @@ -71,6 +71,8 @@ class cMusicManager void PlayFrontEndTrack(uint32, uint8); void PreloadCutSceneMusic(uint32); void PlayPreloadedCutSceneMusic(void); + bool ResumeCutSceneMusicAfterPause(void); + bool IsCutSceneMusicPlaying(void); void StopCutSceneMusic(void); uint32 GetRadioInCar(void); void SetRadioInCar(uint32); diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 2450bea..f4c487c 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -150,6 +150,8 @@ UpdateWindowMinimizedPause(void) RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); CTimer::SetWindowMinimizedPause(backgroundPause); + if (backgroundPause) + CCutsceneMgr::RecordWindowPause(); if (!pause) { ForegroundApp = TRUE; diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 15c1d5d..e9502ec 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -159,6 +159,8 @@ UpdateWindowMinimizedPause(void) RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); CTimer::SetWindowMinimizedPause(backgroundPause); + if (backgroundPause) + CCutsceneMgr::RecordWindowPause(); if (!pause) { ForegroundApp = TRUE; From 1f9edee1c817e20cc41f281c3f4dd335309d0d10 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 02:46:58 +0300 Subject: [PATCH 10/28] fix sound --- src/animation/CutsceneMgr.cpp | 41 ++++++++++++++++++++++++++++++----- src/animation/CutsceneMgr.h | 1 + src/audio/DMAudio.cpp | 6 +++++ src/audio/DMAudio.h | 1 + src/audio/MusicManager.cpp | 9 ++++++++ src/audio/MusicManager.h | 1 + 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp index 46e207d..a1d8f3f 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/animation/CutsceneMgr.cpp @@ -298,6 +298,26 @@ CCutsceneMgr::RecordWindowPause(void) } } +bool +CCutsceneMgr::SyncCutsceneToMusic(void) +{ + if (!ms_cutsceneHasMusic || !bCamLoaded) + return false; + + uint32 finishTime = TheCamera.GetCutSceneFinishTime(); + if (finishTime == 0) + return false; + + int32 musicPosition = DMAudio.GetCutSceneMusicPosition(); + if (musicPosition < 0) + return false; + + uint32 clampedPosition = musicPosition > (int32)finishTime ? finishTime : musicPosition; + TheCamera.SetPercentAlongCutScene(100.0f * (float)clampedPosition / (float)finishTime); + ms_cutsceneTimer = clampedPosition * 0.001f; + return true; +} + void CCutsceneMgr::SetupCutsceneToStart(void) { @@ -562,19 +582,28 @@ CCutsceneMgr::Update(void) if (!ms_running) return; + bool syncedToMusic = false; if (ms_wasWindowPaused) { - if (ms_cutsceneMusicNeedsResume && ms_cutsceneMusicResumeFrames != 0 && !DMAudio.ResumeCutSceneMusicAfterPause()) { - --ms_cutsceneMusicResumeFrames; - CTimer::SetTimeStep(0.0f); - CTimer::SetTimeStepNonClipped(0.0f); - return; + if (ms_cutsceneMusicNeedsResume) { + DMAudio.ResumeCutSceneMusicAfterPause(); + syncedToMusic = SyncCutsceneToMusic(); + if (!syncedToMusic && ms_cutsceneMusicResumeFrames != 0 && TheCamera.GetCutSceneFinishTime() != 0) { + --ms_cutsceneMusicResumeFrames; + CTimer::SetTimeStep(0.0f); + CTimer::SetTimeStepNonClipped(0.0f); + return; + } } ms_wasWindowPaused = false; ms_cutsceneMusicNeedsResume = false; ms_cutsceneMusicResumeFrames = 0; } - ms_cutsceneTimer += CTimer::GetTimeStepNonClippedInSeconds(); + if (!syncedToMusic) + syncedToMusic = SyncCutsceneToMusic(); + + if (!syncedToMusic) + ms_cutsceneTimer += CTimer::GetTimeStepNonClippedInSeconds(); for (int i = 0; i < ms_numCutsceneObjs; i++) { int modelId = ms_pCutsceneObjects[i]->GetModelIndex(); diff --git a/src/animation/CutsceneMgr.h b/src/animation/CutsceneMgr.h index 12bb5ff..f0f64b3 100644 --- a/src/animation/CutsceneMgr.h +++ b/src/animation/CutsceneMgr.h @@ -28,6 +28,7 @@ class CCutsceneMgr static bool ms_cutsceneMusicNeedsResume; static bool ms_wasWindowPaused; static uint32 ms_cutsceneMusicResumeFrames; + static bool SyncCutsceneToMusic(void); public: static CDirectory *ms_pCutsceneDir; static uint32 ms_cutsceneLoadStatus; diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index 64cdf57..c9742f1 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -291,6 +291,12 @@ cDMAudio::IsCutSceneMusicPlaying(void) return MusicManager.IsCutSceneMusicPlaying(); } +int32 +cDMAudio::GetCutSceneMusicPosition(void) +{ + return MusicManager.GetCutSceneMusicPosition(); +} + void cDMAudio::StopCutSceneMusic(void) { diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index e5042e5..c9aae0a 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -81,6 +81,7 @@ class cDMAudio void PlayPreloadedCutSceneMusic(void); bool ResumeCutSceneMusicAfterPause(void); bool IsCutSceneMusicPlaying(void); + int32 GetCutSceneMusicPosition(void); void StopCutSceneMusic(void); void PreloadMissionAudio(uint8 slot, Const char *missionAudio); diff --git a/src/audio/MusicManager.cpp b/src/audio/MusicManager.cpp index 400fe6b..c6884a2 100644 --- a/src/audio/MusicManager.cpp +++ b/src/audio/MusicManager.cpp @@ -1116,6 +1116,15 @@ cMusicManager::IsCutSceneMusicPlaying(void) m_nPlayingTrack != NO_TRACK && SampleManager.IsStreamPlaying(0); } +int32 +cMusicManager::GetCutSceneMusicPosition(void) +{ + if (!IsCutSceneMusicPlaying()) + return -1; + + return SampleManager.GetStreamedFilePosition(0); +} + void cMusicManager::StopCutSceneMusic(void) { diff --git a/src/audio/MusicManager.h b/src/audio/MusicManager.h index 2c5cdbf..8145a9a 100644 --- a/src/audio/MusicManager.h +++ b/src/audio/MusicManager.h @@ -73,6 +73,7 @@ class cMusicManager void PlayPreloadedCutSceneMusic(void); bool ResumeCutSceneMusicAfterPause(void); bool IsCutSceneMusicPlaying(void); + int32 GetCutSceneMusicPosition(void); void StopCutSceneMusic(void); uint32 GetRadioInCar(void); void SetRadioInCar(uint32); From 60a70a6c70a1d7b7456312e32c5bb0a2a03d62c1 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 13:39:56 +0300 Subject: [PATCH 11/28] ble --- src/animation/CutsceneMgr.cpp | 2 +- src/animation/CutsceneMgr.h | 1 + src/core/Game.cpp | 5 +++-- src/skel/glfw/glfw.cpp | 30 ++++++++++++++++------------- src/skel/win/win.cpp | 36 +++++++++++++++++++++++------------ 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp index a1d8f3f..a798774 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/animation/CutsceneMgr.cpp @@ -290,7 +290,7 @@ CCutsceneMgr::FinishCutscene() void CCutsceneMgr::RecordWindowPause(void) { - if (ms_loaded || ms_running || ms_cutsceneProcessing || ms_cutsceneLoadStatus != 0) { + if (IsActiveOrLoadingForPause()) { ms_wasWindowPaused = true; ms_cutsceneMusicNeedsResume = ms_cutsceneMusicNeedsResume || (ms_cutsceneHasMusic && (ms_cutsceneLoadStatus != 0 || DMAudio.IsCutSceneMusicPlaying())); diff --git a/src/animation/CutsceneMgr.h b/src/animation/CutsceneMgr.h index f0f64b3..254dc12 100644 --- a/src/animation/CutsceneMgr.h +++ b/src/animation/CutsceneMgr.h @@ -37,6 +37,7 @@ class CCutsceneMgr static bool IsRunning(void) { return ms_running; } static bool HasLoaded(void) { return ms_loaded; } static bool IsCutsceneProcessing(void) { return ms_cutsceneProcessing; } + static bool IsActiveOrLoadingForPause(void) { return ms_running || ms_cutsceneProcessing || ms_loaded || ms_cutsceneLoadStatus != 0; } static bool WasCutsceneSkipped(void) { return ms_wasCutsceneSkipped; } static bool UseLodMultiplier(void) { return ms_useLodMultiplier; } static CCutsceneObject* GetCutsceneObject(int id) { return ms_pCutsceneObjects[id]; } diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 4168a35..22e3d82 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -78,6 +78,7 @@ #include "User.h" #include "VisibilityPlugins.h" #include "WaterCannon.h" +#include "World.h" #include "WaterLevel.h" #include "Weapon.h" #include "WeaponEffects.h" @@ -1273,8 +1274,8 @@ bool CGame::ShouldUseBackgroundPauseAfterFocusLoss() { return CGame::playingIntro - || CCutsceneMgr::IsRunning() - || CCutsceneMgr::IsCutsceneProcessing() + || CCutsceneMgr::IsActiveOrLoadingForPause() + || CWorld::bProcessCutsceneOnly || TheCamera.m_WideScreenOn || CTheScripts::UseTextCommands != 0 || CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_CUTSCENE) diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index f4c487c..f1af007 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -146,22 +146,23 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) static void UpdateWindowMinimizedPause(void) { - RwBool pause = IsWindowMinimizedPauseActive(); - RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); + RwBool windowPause = IsWindowMinimizedPauseActive(); + RwBool suppressPauseMenu = ShouldUseBackgroundPauseAfterFocusLoss(); RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); - CTimer::SetWindowMinimizedPause(backgroundPause); - if (backgroundPause) - CCutsceneMgr::RecordWindowPause(); - if (!pause) { + if (!windowPause) { + CTimer::SetWindowMinimizedPause(false); ForegroundApp = TRUE; if (WindowAudioSuspended || wasWindowPause) SetWindowAudioSuspended(FALSE, TRUE); return; } - if (ForegroundApp && !backgroundPause) + if (ForegroundApp && !suppressPauseMenu) CGame::InitAfterFocusLoss(); + + CTimer::SetWindowMinimizedPause(true); + CCutsceneMgr::RecordWindowPause(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); } @@ -183,14 +184,19 @@ psRefreshFocusAfterPause(void) void psRestoreFocusAfterPause(void) { - CTimer::SetWindowMinimizedPause(false); WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); - if (!IsWindowMinimizedPauseActive()) { - ForegroundApp = TRUE; - SetWindowAudioSuspended(FALSE, TRUE); + if (IsWindowMinimizedPauseActive()) { + UpdateWindowMinimizedPause(); + return; } + + RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); + CTimer::SetWindowMinimizedPause(false); + ForegroundApp = TRUE; + if (WindowAudioSuspended || wasWindowPause) + SetWindowAudioSuspended(FALSE, TRUE); } size_t _dwMemAvailPhys; @@ -1939,8 +1945,6 @@ cursorEnterCB(GLFWwindow* window, int entered) { void windowFocusCB(GLFWwindow* window, int focused) { WindowFocused = !!focused; - if (!WindowFocused) - CGame::InitAfterFocusLoss(); UpdateWindowMinimizedPause(); } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index e9502ec..8751c9c 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -155,22 +155,23 @@ ShouldUseBackgroundPauseAfterFocusLoss(void) static void UpdateWindowMinimizedPause(void) { - RwBool pause = IsWindowMinimizedPauseActive(); - RwBool backgroundPause = pause && ShouldUseBackgroundPauseAfterFocusLoss(); + RwBool windowPause = IsWindowMinimizedPauseActive(); + RwBool suppressPauseMenu = ShouldUseBackgroundPauseAfterFocusLoss(); RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); - CTimer::SetWindowMinimizedPause(backgroundPause); - if (backgroundPause) - CCutsceneMgr::RecordWindowPause(); - if (!pause) { + if (!windowPause) { + CTimer::SetWindowMinimizedPause(false); ForegroundApp = TRUE; if (WindowAudioSuspended || wasWindowPause) SetWindowAudioSuspended(FALSE, TRUE); return; } - if (ForegroundApp && !backgroundPause) + if (ForegroundApp && !suppressPauseMenu) CGame::InitAfterFocusLoss(); + + CTimer::SetWindowMinimizedPause(true); + CCutsceneMgr::RecordWindowPause(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); } @@ -194,14 +195,19 @@ void psRestoreFocusAfterPause(void) { HWND currentWindow = PSGLOBAL(window); - CTimer::SetWindowMinimizedPause(false); WindowMinimized = IsIconic(currentWindow); WindowActive = GetForegroundWindow() == currentWindow || GetActiveWindow() == currentWindow; - if (!IsWindowMinimizedPauseActive()) { - ForegroundApp = TRUE; - SetWindowAudioSuspended(FALSE, TRUE); + if (IsWindowMinimizedPauseActive()) { + UpdateWindowMinimizedPause(); + return; } + + RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); + CTimer::SetWindowMinimizedPause(false); + ForegroundApp = TRUE; + if (WindowAudioSuspended || wasWindowPause) + SetWindowAudioSuspended(FALSE, TRUE); } #ifdef PS2_MENU @@ -1438,7 +1444,13 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) case WM_SETFOCUS: #endif { - CGame::InitAfterFocusLoss(); +#ifdef FIX_BUGS + WindowActive = FALSE; +#else + WindowActive = TRUE; +#endif + WindowMinimized = IsIconic(window); + UpdateWindowMinimizedPause(); break; } From 2147068514e3bde4a09016dc9711bb187dbaf602 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 13:57:35 +0300 Subject: [PATCH 12/28] bebebe --- src/skel/glfw/glfw.cpp | 12 +----------- src/skel/win/win.cpp | 6 +++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index f1af007..cb7ae4f 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -98,20 +98,10 @@ static psGlobalType PsGlobal; #define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) -static RwBool -IsFullscreenLikeWindowMode(void) -{ -#ifdef IMPROVED_VIDEOMODE - return PSGLOBAL(fullScreen) || FrontEndMenuManager.m_nPrefsWindowed == WINDOWMODE_BORDERLESS; -#else - return PSGLOBAL(fullScreen); -#endif -} - static RwBool IsWindowMinimizedPauseActive(void) { - return WindowIconified || (IsFullscreenLikeWindowMode() && !WindowFocused); + return WindowIconified || !WindowFocused; } static RwBool diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 8751c9c..b31c938 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -143,7 +143,7 @@ IsFullscreenLikeWindowMode(void) static RwBool IsWindowMinimizedPauseActive(void) { - return WindowMinimized || (IsFullscreenLikeWindowMode() && !WindowActive); + return WindowMinimized || !WindowActive; } static RwBool @@ -181,7 +181,7 @@ RefreshWindowActivityState(void) { HWND currentWindow = PSGLOBAL(window); WindowMinimized = IsIconic(currentWindow); - WindowActive = GetForegroundWindow() == currentWindow || GetActiveWindow() == currentWindow; + WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; UpdateWindowMinimizedPause(); } @@ -196,7 +196,7 @@ psRestoreFocusAfterPause(void) { HWND currentWindow = PSGLOBAL(window); WindowMinimized = IsIconic(currentWindow); - WindowActive = GetForegroundWindow() == currentWindow || GetActiveWindow() == currentWindow; + WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; if (IsWindowMinimizedPauseActive()) { UpdateWindowMinimizedPause(); From 438cc955f8c8fd6329845a332552b9ece48c06a8 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 14:22:02 +0300 Subject: [PATCH 13/28] e --- src/audio/AudioLogic.cpp | 7 ++++++- src/audio/PoliceRadio.cpp | 5 ++--- src/core/main.cpp | 11 +++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/audio/AudioLogic.cpp b/src/audio/AudioLogic.cpp index 0694f14..01e1a40 100644 --- a/src/audio/AudioLogic.cpp +++ b/src/audio/AudioLogic.cpp @@ -9926,7 +9926,12 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) break; } if (MissionScriptAudioUsesPoliceChannel(m_sMissionAudio.m_nSampleIndex[slot])) { - if (!m_nUserPause) { + if (m_nUserPause) { + SampleManager.PauseStream(1, 1); + nFramesUntilFailedResume[slot] = 0; + } else { + if (m_nPreviousUserPause) + SampleManager.PauseStream(0, 1); if (nCheckPlayingDelay[slot]) { --nCheckPlayingDelay[slot]; } else if (GetMissionScriptPoliceAudioPlayingStatus() == PLAY_STATUS_FINISHED || m_sMissionAudio.m_nMissionAudioCounter[slot]-- == 0) { diff --git a/src/audio/PoliceRadio.cpp b/src/audio/PoliceRadio.cpp index 227d27d..5521499 100644 --- a/src/audio/PoliceRadio.cpp +++ b/src/audio/PoliceRadio.cpp @@ -169,13 +169,12 @@ cAudioManager::ServicePoliceRadioChannel(uint8 wantedLevel) if (m_nUserPause != 0) { if (SampleManager.GetChannelUsedFlag(policeChannel)) SampleManager.StopChannel(policeChannel); - if (g_nMissionAudioSfx != NO_SAMPLE && bMissionAudioPhysicalPlayingStatus == 1 && - SampleManager.IsStreamPlaying(1)) { + if (g_nMissionAudioSfx != NO_SAMPLE && g_nMissionAudioPlayingStatus == 1) { SampleManager.PauseStream(1, 1); } } else { if (m_nPreviousUserPause && g_nMissionAudioSfx != NO_SAMPLE && - bMissionAudioPhysicalPlayingStatus == 1) { + g_nMissionAudioPlayingStatus == 1) { SampleManager.PauseStream(0, 1); } if (m_sPoliceRadioQueue.policeChannelTimer == 0) bChannelOpen = false; diff --git a/src/core/main.cpp b/src/core/main.cpp index e1028d5..5bb9907 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -1456,6 +1456,11 @@ Idle(void *arg) psRefreshFocusAfterPause(); CTimer::Update(); + if (CTimer::GetIsWindowMinimizedPaused()) { + DMAudio.Service(); + return; + } + tbInit(); CSprite2d::InitPerFrame(); @@ -1624,6 +1629,12 @@ FrontendIdle(void) CDraw::CalculateAspectRatio(); psRefreshFocusAfterPause(); CTimer::Update(); + + if (CTimer::GetIsWindowMinimizedPaused()) { + DMAudio.Service(); + return; + } + CSprite2d::SetRecipNearClip(); // this should be on InitialiseRenderWare according to PS2 asm. seems like a bug fix CSprite2d::InitPerFrame(); CFont::InitPerFrame(); From ca0bcbfd5b68a8c6631fbd2bdcb8349b21907580 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 14:40:00 +0300 Subject: [PATCH 14/28] fix black screen --- src/audio/DMAudio.cpp | 7 +++++++ src/audio/DMAudio.h | 1 + src/core/main.cpp | 20 ++++++++++++++++++++ src/skel/glfw/glfw.cpp | 22 ++++++++++++++++++++++ src/skel/win/win.cpp | 22 ++++++++++++++++++++++ 5 files changed, 72 insertions(+) diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index c9742f1..a06c501 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -26,6 +26,13 @@ cDMAudio::Service(void) AudioManager.Service(); } +void +cDMAudio::SetStreamsPausedForWindowPause(bool paused) +{ + for (uint8 i = 0; i < MAX_STREAMS; i++) + SampleManager.PauseStream(paused ? 1 : 0, i); +} + int32 cDMAudio::CreateEntity(eAudioType type, void *UID) { diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index c9aae0a..d541f6e 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -22,6 +22,7 @@ class cDMAudio void Initialise(void); void Terminate(void); void Service(void); + void SetStreamsPausedForWindowPause(bool paused); int32 CreateEntity(eAudioType type, void *UID); void DestroyEntity(int32 audioEntity); diff --git a/src/core/main.cpp b/src/core/main.cpp index 5bb9907..6ec222b 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -579,11 +579,26 @@ ResetLoadingScreenBar() NumberOfChunksLoaded = 0.0f; } +static bool +PauseLoadingScreenForWindowMinimized(void) +{ + psRefreshFocusAfterPause(); + if (!CTimer::GetIsWindowMinimizedPaused()) + return false; + + DMAudio.SetStreamsPausedForWindowPause(true); + DMAudio.Service(); + return true; +} + void LoadingScreen(const char *str1, const char *str2, const char *splashscreen) { CSprite2d *splash; + if (PauseLoadingScreenForWindowMinimized()) + return; + #ifdef DISABLE_LOADING_SCREEN if (str1 && str2) return; @@ -670,6 +685,9 @@ LoadingIslandScreen(const char *levelName) { CSprite2d *splash; + if (PauseLoadingScreenForWindowMinimized()) + return; + splash = LoadSplash(nil); if(!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) return; @@ -1457,6 +1475,7 @@ Idle(void *arg) CTimer::Update(); if (CTimer::GetIsWindowMinimizedPaused()) { + DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.Service(); return; } @@ -1631,6 +1650,7 @@ FrontendIdle(void) CTimer::Update(); if (CTimer::GetIsWindowMinimizedPaused()) { + DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.Service(); return; } diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index cb7ae4f..fbd91c4 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -120,6 +120,7 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (suspend) { _InputShutdownMouse(); + DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); @@ -129,6 +130,7 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + DMAudio.SetStreamsPausedForWindowPause(false); DMAudio.Service(); } } @@ -2288,6 +2290,10 @@ main(int argc, char *argv[]) case GS_INIT_ONCE: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + //CoUninitialize(); #ifdef PS2_MENU @@ -2316,6 +2322,10 @@ main(int argc, char *argv[]) #ifndef PS2_MENU case GS_INIT_FRONTEND: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + LoadingScreen(nil, nil, "loadsc0"); // LoadingScreen(nil, nil, "loadsc0"); // duplicate @@ -2337,9 +2347,17 @@ main(int argc, char *argv[]) case GS_FRONTEND: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + if(!WindowIconified) RsEventHandler(rsFRONTENDIDLE, nil); + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + #ifdef PS2_MENU if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) #else @@ -2367,6 +2385,10 @@ main(int argc, char *argv[]) case GS_INIT_PLAYING_GAME: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + #ifdef PS2_MENU CGame::Initialise("DATA\\GTA3.DAT"); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index b31c938..306f4c5 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -118,6 +118,7 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) if (suspend) { ReleaseWindowMouseFocus(); + DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); @@ -126,6 +127,7 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); + DMAudio.SetStreamsPausedForWindowPause(false); DMAudio.Service(); } } @@ -2530,6 +2532,10 @@ WinMain(HINSTANCE instance, case GS_INIT_ONCE: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + #ifdef NO_MOVIES if (!gbNoMovies) #endif @@ -2570,6 +2576,10 @@ WinMain(HINSTANCE instance, #ifndef PS2_MENU case GS_INIT_FRONTEND: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + LoadingScreen(nil, nil, "loadsc0"); // LoadingScreen(nil, nil, "loadsc0"); // duplicate @@ -2591,11 +2601,19 @@ WinMain(HINSTANCE instance, case GS_FRONTEND: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + GetWindowPlacement(PSGLOBAL(window), &wp); if (wp.showCmd != SW_SHOWMINIMIZED) RsEventHandler(rsFRONTENDIDLE, nil); + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + #ifdef PS2_MENU if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) #else @@ -2623,6 +2641,10 @@ WinMain(HINSTANCE instance, case GS_INIT_PLAYING_GAME: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + #ifdef PS2_MENU CGame::Initialise("DATA\\GTA3.DAT"); From 6654012d6362703ea2d6d8649dbf0919e6125511 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 14:54:29 +0300 Subject: [PATCH 15/28] .. --- src/core/Game.cpp | 19 +++++++++++++++---- src/save/GenericGameStorage.cpp | 4 +++- src/skel/glfw/glfw.cpp | 8 +++++++- src/skel/win/win.cpp | 8 +++++++- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 22e3d82..f444392 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -1,6 +1,7 @@ #include "common.h" #include "platform.h" +#include "crossplatform.h" #include "Game.h" #include "main.h" #include "RwHelper.h" @@ -539,8 +540,10 @@ bool CGame::Initialise(const char* datFile) #endif { CTheScripts::StartTestScript(); - CTheScripts::Process(); - TheCamera.Process(); + if (!CTimer::GetIsWindowMinimizedPaused()) { + CTheScripts::Process(); + TheCamera.Process(); + } } LoadingScreen("Loading the Game", "Load scene", nil); @@ -675,6 +678,7 @@ void CGame::ReInitGameObjectVariables(void) POP_MEMID(); CTimer::Initialise(); + psRefreshFocusAfterPause(); CClock::Initialise(1000); CTheCarGenerators::Init(); CHeli::InitHelis(); @@ -698,8 +702,10 @@ void CGame::ReInitGameObjectVariables(void) { CCranes::InitCranes(); CTheScripts::StartTestScript(); - CTheScripts::Process(); - TheCamera.Process(); + if (!CTimer::GetIsWindowMinimizedPaused()) { + CTheScripts::Process(); + TheCamera.Process(); + } CTrain::InitTrains(); CPlane::InitPlanes(); } @@ -748,6 +754,7 @@ void CGame::InitialiseWhenRestarting(void) CRGBA color(255, 255, 255, 255); CTimer::Initialise(); + psRefreshFocusAfterPause(); CSprite2d::SetRecipNearClip(); if (b_FoundRecentSavedGameWantToLoad || FrontEndMenuManager.m_bWantToLoad) @@ -800,6 +807,7 @@ void CGame::InitialiseWhenRestarting(void) ShutDownForRestart(); CTimer::Stop(); CTimer::Initialise(); + psRefreshFocusAfterPause(); FrontEndMenuManager.m_bWantToLoad = false; ReInitGameObjectVariables(); currLevel = LEVEL_GENERIC; @@ -820,6 +828,9 @@ void CGame::InitialiseWhenRestarting(void) void CGame::Process(void) { + if (CTimer::GetIsWindowMinimizedPaused()) + return; + CPad::UpdatePads(); #ifdef USE_CUSTOM_ALLOCATOR ProcessTidyUpMemory(); diff --git a/src/save/GenericGameStorage.cpp b/src/save/GenericGameStorage.cpp index 96a2cc0..1481b60 100644 --- a/src/save/GenericGameStorage.cpp +++ b/src/save/GenericGameStorage.cpp @@ -474,7 +474,9 @@ DoGameSpecificStuffAfterSucessLoad() StillToFadeOut = true; JustLoadedDontFadeInYet = true; TheCamera.Fade(0.0f, 0); - CTheScripts::Process(); + psRefreshFocusAfterPause(); + if (!CTimer::GetIsWindowMinimizedPaused()) + CTheScripts::Process(); } bool diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index fbd91c4..742d246 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -1475,7 +1475,9 @@ void resizeCB(GLFWwindow* window, int width, int height) { if (RwInitialised && gGameState == GS_PLAYING_GAME) { - RsEventHandler(rsIDLE, (void *)TRUE); + RefreshWindowActivityState(); + if (!IsWindowMinimizedPauseActive()) + RsEventHandler(rsIDLE, (void *)TRUE); } if (RwInitialised && height > 0 && width > 0) { @@ -2423,6 +2425,10 @@ main(int argc, char *argv[]) case GS_PLAYING_GAME: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); if ( RwInitialised ) { diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 306f4c5..4495208 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -1154,7 +1154,9 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) if (RwInitialised && gGameState == GS_PLAYING_GAME) { - RsEventHandler(rsIDLE, (void *)TRUE); + RefreshWindowActivityState(); + if (!IsWindowMinimizedPauseActive()) + RsEventHandler(rsIDLE, (void *)TRUE); } /* Manually resize window */ @@ -2679,6 +2681,10 @@ WinMain(HINSTANCE instance, case GS_PLAYING_GAME: { + RefreshWindowActivityState(); + if (IsWindowMinimizedPauseActive()) + break; + float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); if ( RwInitialised ) { From 77b81888c0f782e75291612750c0948a17fd008c Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 15:13:14 +0300 Subject: [PATCH 16/28] .... --- src/control/Script.cpp | 25 +++++++++++++++++++++-- src/core/Game.cpp | 9 ++++++--- src/core/main.cpp | 11 +++++------ src/save/GenericGameStorage.cpp | 3 +-- src/skel/crossplatform.h | 1 + src/skel/glfw/glfw.cpp | 35 ++++++++++++++++----------------- src/skel/win/win.cpp | 35 ++++++++++++++++----------------- 7 files changed, 70 insertions(+), 49 deletions(-) diff --git a/src/control/Script.cpp b/src/control/Script.cpp index f1a9f35..6e20b14 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -1,5 +1,6 @@ #include "common.h" +#include "crossplatform.h" #include "Script.h" #include "ScriptCommands.h" @@ -137,6 +138,17 @@ int MissionSkipLevel; #endif +static bool +PauseScriptProcessingForWindowMinimized(void) +{ + if (!psRefreshAndGetWindowMinimizedPause()) + return false; + + DMAudio.SetStreamsPausedForWindowPause(true); + DMAudio.Service(); + return true; +} + #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #define REGISTER_COMMAND(command, in, out, cond, ovrd, visual) { command, #command, in, out, cond, ovrd, visual } @@ -2365,6 +2377,9 @@ CRunningScript* CTheScripts::StartNewScript(uint32 ip) void CTheScripts::Process() { + if (PauseScriptProcessingForWindowMinimized()) + return; + if (CReplay::IsPlayingBack()) return; CommandsExecuted = 0; @@ -2469,6 +2484,9 @@ bool CTheScripts::IsPlayerOnAMission() void CRunningScript::Process() { + if (PauseScriptProcessingForWindowMinimized()) + return; + #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT PrintToLog("\n\nProcessing script %s (id %d)\n\n", m_abScriptName, this - CTheScripts::ScriptsArray); #endif @@ -2477,7 +2495,7 @@ void CRunningScript::Process() if (m_bMissionFlag && CTheScripts::FailCurrentMission == 1 && m_nStackPointer == 1) SetIP(m_anStack[--m_nStackPointer]); if (CTimer::GetTimeInMilliseconds() >= m_nWakeTime){ - while (!ProcessOneCommand()) + while (!PauseScriptProcessingForWindowMinimized() && !ProcessOneCommand()) ; return; } @@ -4843,7 +4861,10 @@ CTheScripts::SwitchToMission(int32 mission) pScript->m_nWakeTime = 0; pScript->m_bDeatharrestExecuted = true; - while (!pScript->ProcessOneCommand()); + if (PauseScriptProcessingForWindowMinimized()) + return; + while (!PauseScriptProcessingForWindowMinimized() && !pScript->ProcessOneCommand()) + ; CMessages::ClearMessages(); } diff --git a/src/core/Game.cpp b/src/core/Game.cpp index f444392..5a7dac8 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -540,7 +540,7 @@ bool CGame::Initialise(const char* datFile) #endif { CTheScripts::StartTestScript(); - if (!CTimer::GetIsWindowMinimizedPaused()) { + if (!psRefreshAndGetWindowMinimizedPause()) { CTheScripts::Process(); TheCamera.Process(); } @@ -702,7 +702,7 @@ void CGame::ReInitGameObjectVariables(void) { CCranes::InitCranes(); CTheScripts::StartTestScript(); - if (!CTimer::GetIsWindowMinimizedPaused()) { + if (!psRefreshAndGetWindowMinimizedPause()) { CTheScripts::Process(); TheCamera.Process(); } @@ -828,8 +828,11 @@ void CGame::InitialiseWhenRestarting(void) void CGame::Process(void) { - if (CTimer::GetIsWindowMinimizedPaused()) + if (psRefreshAndGetWindowMinimizedPause() || CTimer::GetIsWindowMinimizedPaused()) { + DMAudio.SetStreamsPausedForWindowPause(true); + DMAudio.Service(); return; + } CPad::UpdatePads(); #ifdef USE_CUSTOM_ALLOCATOR diff --git a/src/core/main.cpp b/src/core/main.cpp index 6ec222b..da1b505 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -582,8 +582,7 @@ ResetLoadingScreenBar() static bool PauseLoadingScreenForWindowMinimized(void) { - psRefreshFocusAfterPause(); - if (!CTimer::GetIsWindowMinimizedPaused()) + if (!psRefreshAndGetWindowMinimizedPause()) return false; DMAudio.SetStreamsPausedForWindowPause(true); @@ -1471,10 +1470,10 @@ Render2dStuffAfterFade(void) void Idle(void *arg) { - psRefreshFocusAfterPause(); + bool windowPause = psRefreshAndGetWindowMinimizedPause(); CTimer::Update(); - if (CTimer::GetIsWindowMinimizedPaused()) { + if (windowPause || CTimer::GetIsWindowMinimizedPaused()) { DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.Service(); return; @@ -1646,10 +1645,10 @@ void FrontendIdle(void) { CDraw::CalculateAspectRatio(); - psRefreshFocusAfterPause(); + bool windowPause = psRefreshAndGetWindowMinimizedPause(); CTimer::Update(); - if (CTimer::GetIsWindowMinimizedPaused()) { + if (windowPause || CTimer::GetIsWindowMinimizedPaused()) { DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.Service(); return; diff --git a/src/save/GenericGameStorage.cpp b/src/save/GenericGameStorage.cpp index 1481b60..fa817c0 100644 --- a/src/save/GenericGameStorage.cpp +++ b/src/save/GenericGameStorage.cpp @@ -474,8 +474,7 @@ DoGameSpecificStuffAfterSucessLoad() StillToFadeOut = true; JustLoadedDontFadeInYet = true; TheCamera.Fade(0.0f, 0); - psRefreshFocusAfterPause(); - if (!CTimer::GetIsWindowMinimizedPaused()) + if (!psRefreshAndGetWindowMinimizedPause()) CTheScripts::Process(); } diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h index b6674dc..8e9859e 100644 --- a/src/skel/crossplatform.h +++ b/src/skel/crossplatform.h @@ -99,6 +99,7 @@ enum eGameState extern RwUInt32 gGameState; RwBool IsForegroundApp(); +RwBool psRefreshAndGetWindowMinimizedPause(void); void psRefreshFocusAfterPause(void); void psRestoreFocusAfterPause(void); diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 742d246..9df5569 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -159,18 +159,25 @@ UpdateWindowMinimizedPause(void) SetWindowAudioSuspended(TRUE); } -static void +static RwBool RefreshWindowActivityState(void) { WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); UpdateWindowMinimizedPause(); + return IsWindowMinimizedPauseActive(); +} + +RwBool +psRefreshAndGetWindowMinimizedPause(void) +{ + return RefreshWindowActivityState(); } void psRefreshFocusAfterPause(void) { - RefreshWindowActivityState(); + psRefreshAndGetWindowMinimizedPause(); } void @@ -1475,8 +1482,7 @@ void resizeCB(GLFWwindow* window, int width, int height) { if (RwInitialised && gGameState == GS_PLAYING_GAME) { - RefreshWindowActivityState(); - if (!IsWindowMinimizedPauseActive()) + if (!psRefreshAndGetWindowMinimizedPause()) RsEventHandler(rsIDLE, (void *)TRUE); } @@ -2292,8 +2298,7 @@ main(int argc, char *argv[]) case GS_INIT_ONCE: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; //CoUninitialize(); @@ -2324,8 +2329,7 @@ main(int argc, char *argv[]) #ifndef PS2_MENU case GS_INIT_FRONTEND: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; LoadingScreen(nil, nil, "loadsc0"); @@ -2349,15 +2353,13 @@ main(int argc, char *argv[]) case GS_FRONTEND: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; if(!WindowIconified) RsEventHandler(rsFRONTENDIDLE, nil); - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; #ifdef PS2_MENU @@ -2387,8 +2389,7 @@ main(int argc, char *argv[]) case GS_INIT_PLAYING_GAME: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; #ifdef PS2_MENU @@ -2425,8 +2426,7 @@ main(int argc, char *argv[]) case GS_PLAYING_GAME: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); @@ -2441,8 +2441,7 @@ main(int argc, char *argv[]) } else { - RefreshWindowActivityState(); - if (!IsWindowMinimizedPauseActive()) + if (!psRefreshAndGetWindowMinimizedPause()) { ForegroundApp = TRUE; SetWindowAudioSuspended(FALSE, TRUE); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 4495208..40456c4 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -178,19 +178,26 @@ UpdateWindowMinimizedPause(void) SetWindowAudioSuspended(TRUE); } -static void +static RwBool RefreshWindowActivityState(void) { HWND currentWindow = PSGLOBAL(window); WindowMinimized = IsIconic(currentWindow); WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; UpdateWindowMinimizedPause(); + return IsWindowMinimizedPauseActive(); +} + +RwBool +psRefreshAndGetWindowMinimizedPause(void) +{ + return RefreshWindowActivityState(); } void psRefreshFocusAfterPause(void) { - RefreshWindowActivityState(); + psRefreshAndGetWindowMinimizedPause(); } void @@ -1154,8 +1161,7 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) if (RwInitialised && gGameState == GS_PLAYING_GAME) { - RefreshWindowActivityState(); - if (!IsWindowMinimizedPauseActive()) + if (!psRefreshAndGetWindowMinimizedPause()) RsEventHandler(rsIDLE, (void *)TRUE); } @@ -2534,8 +2540,7 @@ WinMain(HINSTANCE instance, case GS_INIT_ONCE: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; #ifdef NO_MOVIES @@ -2578,8 +2583,7 @@ WinMain(HINSTANCE instance, #ifndef PS2_MENU case GS_INIT_FRONTEND: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; LoadingScreen(nil, nil, "loadsc0"); @@ -2603,8 +2607,7 @@ WinMain(HINSTANCE instance, case GS_FRONTEND: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; GetWindowPlacement(PSGLOBAL(window), &wp); @@ -2612,8 +2615,7 @@ WinMain(HINSTANCE instance, if (wp.showCmd != SW_SHOWMINIMIZED) RsEventHandler(rsFRONTENDIDLE, nil); - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; #ifdef PS2_MENU @@ -2643,8 +2645,7 @@ WinMain(HINSTANCE instance, case GS_INIT_PLAYING_GAME: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; #ifdef PS2_MENU @@ -2681,8 +2682,7 @@ WinMain(HINSTANCE instance, case GS_PLAYING_GAME: { - RefreshWindowActivityState(); - if (IsWindowMinimizedPauseActive()) + if (psRefreshAndGetWindowMinimizedPause()) break; float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); @@ -2697,8 +2697,7 @@ WinMain(HINSTANCE instance, } else { - RefreshWindowActivityState(); - if (!IsWindowMinimizedPauseActive()) + if (!psRefreshAndGetWindowMinimizedPause()) { ForegroundApp = TRUE; SetWindowAudioSuspended(FALSE, TRUE); From cd1747612d18cd3a64f50112007c669e661ed68b Mon Sep 17 00:00:00 2001 From: tutbylya Date: Sun, 28 Jun 2026 15:24:17 +0300 Subject: [PATCH 17/28] ... --- src/skel/glfw/glfw.cpp | 23 +++++++++++++++++++++-- src/skel/win/win.cpp | 21 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 9df5569..bf50494 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -75,6 +75,7 @@ rw::EngineOpenParams openParams; static RwBool ForegroundApp = TRUE; static RwBool WindowIconified = FALSE; static RwBool WindowFocused = TRUE; +static RwBool WindowPauseLatched = FALSE; static RwBool WindowAudioSuspended = FALSE; static RwBool RwInitialised = FALSE; @@ -101,7 +102,7 @@ static psGlobalType PsGlobal; static RwBool IsWindowMinimizedPauseActive(void) { - return WindowIconified || !WindowFocused; + return WindowPauseLatched || WindowIconified || !WindowFocused; } static RwBool @@ -160,10 +161,14 @@ UpdateWindowMinimizedPause(void) } static RwBool -RefreshWindowActivityState(void) +RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) { WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); + if (WindowIconified || !WindowFocused) + WindowPauseLatched = TRUE; + else if (allowClearPauseLatch) + WindowPauseLatched = FALSE; UpdateWindowMinimizedPause(); return IsWindowMinimizedPauseActive(); } @@ -185,6 +190,10 @@ psRestoreFocusAfterPause(void) { WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); + if (!WindowIconified && WindowFocused) + WindowPauseLatched = FALSE; + else + WindowPauseLatched = TRUE; if (IsWindowMinimizedPauseActive()) { UpdateWindowMinimizedPause(); @@ -287,6 +296,7 @@ psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { + WindowPauseLatched = TRUE; RefreshWindowActivityState(); return FALSE; } @@ -443,6 +453,7 @@ psInitialize(void) PsGlobal.cursorIsInWindow = FALSE; WindowFocused = TRUE; WindowIconified = FALSE; + WindowPauseLatched = FALSE; WindowAudioSuspended = FALSE; PsGlobal.joy1id = -1; @@ -1945,12 +1956,20 @@ cursorEnterCB(GLFWwindow* window, int entered) { void windowFocusCB(GLFWwindow* window, int focused) { WindowFocused = !!focused; + if (!WindowFocused) + WindowPauseLatched = TRUE; + else if (!WindowIconified) + WindowPauseLatched = FALSE; UpdateWindowMinimizedPause(); } void windowIconifyCB(GLFWwindow* window, int iconified) { WindowIconified = !!iconified; + if (WindowIconified) + WindowPauseLatched = TRUE; + else if (WindowFocused) + WindowPauseLatched = FALSE; UpdateWindowMinimizedPause(); } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 40456c4..9562765 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -57,6 +57,7 @@ static RwBool ForegroundApp = TRUE; static RwBool WindowMinimized = FALSE; static RwBool WindowActive = TRUE; +static RwBool WindowPauseLatched = FALSE; static RwBool WindowAudioSuspended = FALSE; static RwBool RwInitialised = FALSE; @@ -145,7 +146,7 @@ IsFullscreenLikeWindowMode(void) static RwBool IsWindowMinimizedPauseActive(void) { - return WindowMinimized || !WindowActive; + return WindowPauseLatched || WindowMinimized || !WindowActive; } static RwBool @@ -179,11 +180,15 @@ UpdateWindowMinimizedPause(void) } static RwBool -RefreshWindowActivityState(void) +RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) { HWND currentWindow = PSGLOBAL(window); WindowMinimized = IsIconic(currentWindow); WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; + if (WindowMinimized || !WindowActive) + WindowPauseLatched = TRUE; + else if (allowClearPauseLatch) + WindowPauseLatched = FALSE; UpdateWindowMinimizedPause(); return IsWindowMinimizedPauseActive(); } @@ -206,6 +211,10 @@ psRestoreFocusAfterPause(void) HWND currentWindow = PSGLOBAL(window); WindowMinimized = IsIconic(currentWindow); WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; + if (!WindowMinimized && WindowActive) + WindowPauseLatched = FALSE; + else + WindowPauseLatched = TRUE; if (IsWindowMinimizedPauseActive()) { UpdateWindowMinimizedPause(); @@ -347,6 +356,7 @@ psCameraBeginUpdate(RwCamera *camera) { WindowActive = FALSE; WindowMinimized = IsIconic(PSGLOBAL(window)); + WindowPauseLatched = TRUE; UpdateWindowMinimizedPause(); return FALSE; } @@ -752,6 +762,7 @@ psInitialize(void) PsGlobal.fullScreen = FALSE; WindowMinimized = FALSE; WindowActive = TRUE; + WindowPauseLatched = FALSE; WindowAudioSuspended = FALSE; PsGlobal.dinterface = nil; @@ -1109,6 +1120,8 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { RwRect r; WindowMinimized = wParam == SIZE_MINIMIZED || IsIconic(window); + if (WindowMinimized) + WindowPauseLatched = TRUE; UpdateWindowMinimizedPause(); r.x = 0; @@ -1305,6 +1318,7 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { WindowActive = (BOOL)wParam; WindowMinimized = IsIconic(window); + WindowPauseLatched = !WindowActive || WindowMinimized; UpdateWindowMinimizedPause(); switch ( gGameState ) @@ -1369,6 +1383,7 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { WindowActive = LOWORD(wParam) != WA_INACTIVE; WindowMinimized = HIWORD(wParam) || IsIconic(window); + WindowPauseLatched = !WindowActive || WindowMinimized; UpdateWindowMinimizedPause(); return 0L; @@ -1456,8 +1471,10 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { #ifdef FIX_BUGS WindowActive = FALSE; + WindowPauseLatched = TRUE; #else WindowActive = TRUE; + WindowPauseLatched = IsIconic(window); #endif WindowMinimized = IsIconic(window); UpdateWindowMinimizedPause(); From d4d1f256bc59063d56ba2bb698ae44ae32ec417c Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 01:02:18 +0300 Subject: [PATCH 18/28] i am back --- src/core/Camera.cpp | 3 +++ src/core/Timer.cpp | 30 ++++++++++++++++++++++-- src/core/World.cpp | 3 +++ src/skel/glfw/glfw.cpp | 53 ++++++++++++++++++++++++++++++------------ src/skel/win/win.cpp | 43 ++++++++++++++++++++++------------ 5 files changed, 100 insertions(+), 32 deletions(-) diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp index 41d80ed..63b9f76 100644 --- a/src/core/Camera.cpp +++ b/src/core/Camera.cpp @@ -253,6 +253,9 @@ CCamera::Init(void) void CCamera::Process(void) { + if (CTimer::GetIsWindowMinimizedPaused()) + return; + // static bool InterpolatorNotInitialised = true; // unused static float PlayerMinDist = 1.3f; static bool WasPreviouslyInterSyhonFollowPed = false; // only written diff --git a/src/core/Timer.cpp b/src/core/Timer.cpp index d7d0ac2..45b2c3f 100644 --- a/src/core/Timer.cpp +++ b/src/core/Timer.cpp @@ -43,13 +43,16 @@ double frameTime; void CTimer::Initialise(void) { debug("Initialising CTimer...\n"); + // A loading/restart path may recreate the timer while the game is minimized. + // Keep the platform pause alive so the next frame cannot advance silently. + bool wasWindowMinimizedPaused = m_WindowMinimizedPause; ms_fTimeScale = 1.0f; ms_fTimeStep = 1.0f; suspendDepth = 0; m_UserPause = false; m_CodePause = false; - m_WindowMinimizedPause = false; + m_WindowMinimizedPause = wasWindowMinimizedPaused; m_snTimeInMillisecondsNonClipped = 0; m_snPreviousTimeInMilliseconds = 0; m_snTimeInMilliseconds = 1; @@ -75,6 +78,9 @@ void CTimer::Initialise(void) m_FrameCounter = 0; DMAudio.ResetTimers(m_snPreviousTimeInMilliseconds); + + if (m_WindowMinimizedPause) + Suspend(); debug("CTimer ready\n"); } @@ -87,6 +93,26 @@ void CTimer::Shutdown(void) void CTimer::Update(void) { m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; + + // Window pause freezes the complete game clock, including pause-mode time and + // the frame counter. Scripted cutscenes use both of those independently of + // the normal time step. + if (m_WindowMinimizedPause) { +#ifdef _WIN32 + if ((double)_nCyclesPerMS != 0.0) { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + _oldPerfCounter = pc; + } else +#endif + { + oldPcTimer = RsTimer(); + } + + ms_fTimeStep = 0.0f; + ms_fTimeStepNonClipped = 0.0f; + return; + } #ifdef _WIN32 if ( (double)_nCyclesPerMS != 0.0 ) @@ -181,7 +207,7 @@ void CTimer::Suspend(void) void CTimer::Resume(void) { - if ( --suspendDepth != 0 ) + if (suspendDepth == 0 || --suspendDepth != 0) return; #ifdef _WIN32 diff --git a/src/core/World.cpp b/src/core/World.cpp index fb854c7..d4bdfcc 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -1921,6 +1921,9 @@ CWorld::RemoveStaticObjects() void CWorld::Process(void) { + if (CTimer::GetIsWindowMinimizedPaused()) + return; + if(!(CTimer::GetFrameCounter() & 63)) CReferences::PruneAllReferencesInWorld(); if(bProcessCutsceneOnly) { diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index bf50494..54e2b55 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -105,6 +105,27 @@ IsWindowMinimizedPauseActive(void) return WindowPauseLatched || WindowIconified || !WindowFocused; } +static void +ReadWindowActivityState(void) +{ + GLFWwindow *window = PSGLOBAL(window); + if (window == nil) + return; + + WindowIconified = glfwGetWindowAttrib(window, GLFW_ICONIFIED); + WindowFocused = glfwGetWindowAttrib(window, GLFW_FOCUSED); + +#ifdef _WIN32 + // GLFW can briefly report a fullscreen window as focused while it is being + // minimized. The native foreground window is authoritative on Windows. + HWND nativeWindow = glfwGetWin32Window(window); + if (nativeWindow != nil) { + WindowIconified = IsIconic(nativeWindow); + WindowFocused = !WindowIconified && GetForegroundWindow() == nativeWindow; + } +#endif +} + static RwBool ShouldUseBackgroundPauseAfterFocusLoss(void) { @@ -163,8 +184,7 @@ UpdateWindowMinimizedPause(void) static RwBool RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) { - WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); - WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); + ReadWindowActivityState(); if (WindowIconified || !WindowFocused) WindowPauseLatched = TRUE; else if (allowClearPauseLatch) @@ -176,7 +196,7 @@ RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) RwBool psRefreshAndGetWindowMinimizedPause(void) { - return RefreshWindowActivityState(); + return RefreshWindowActivityState(TRUE); } void @@ -188,8 +208,7 @@ psRefreshFocusAfterPause(void) void psRestoreFocusAfterPause(void) { - WindowIconified = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_ICONIFIED); - WindowFocused = glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED); + ReadWindowActivityState(); if (!WindowIconified && WindowFocused) WindowPauseLatched = FALSE; else @@ -1955,22 +1974,26 @@ cursorEnterCB(GLFWwindow* window, int entered) { void windowFocusCB(GLFWwindow* window, int focused) { - WindowFocused = !!focused; - if (!WindowFocused) + if (!focused) { + WindowFocused = FALSE; WindowPauseLatched = TRUE; - else if (!WindowIconified) - WindowPauseLatched = FALSE; - UpdateWindowMinimizedPause(); + UpdateWindowMinimizedPause(); + return; + } + + RefreshWindowActivityState(TRUE); } void windowIconifyCB(GLFWwindow* window, int iconified) { - WindowIconified = !!iconified; - if (WindowIconified) + if (iconified) { + WindowIconified = TRUE; WindowPauseLatched = TRUE; - else if (WindowFocused) - WindowPauseLatched = FALSE; - UpdateWindowMinimizedPause(); + UpdateWindowMinimizedPause(); + return; + } + + RefreshWindowActivityState(TRUE); } /* diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 9562765..b44063a 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -149,6 +149,14 @@ IsWindowMinimizedPauseActive(void) return WindowPauseLatched || WindowMinimized || !WindowActive; } +static void +ReadWindowActivityState(void) +{ + HWND currentWindow = PSGLOBAL(window); + WindowMinimized = IsIconic(currentWindow); + WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; +} + static RwBool ShouldUseBackgroundPauseAfterFocusLoss(void) { @@ -182,9 +190,7 @@ UpdateWindowMinimizedPause(void) static RwBool RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) { - HWND currentWindow = PSGLOBAL(window); - WindowMinimized = IsIconic(currentWindow); - WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; + ReadWindowActivityState(); if (WindowMinimized || !WindowActive) WindowPauseLatched = TRUE; else if (allowClearPauseLatch) @@ -196,7 +202,7 @@ RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) RwBool psRefreshAndGetWindowMinimizedPause(void) { - return RefreshWindowActivityState(); + return RefreshWindowActivityState(TRUE); } void @@ -208,9 +214,7 @@ psRefreshFocusAfterPause(void) void psRestoreFocusAfterPause(void) { - HWND currentWindow = PSGLOBAL(window); - WindowMinimized = IsIconic(currentWindow); - WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; + ReadWindowActivityState(); if (!WindowMinimized && WindowActive) WindowPauseLatched = FALSE; else @@ -1316,10 +1320,15 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) case WM_ACTIVATEAPP: { - WindowActive = (BOOL)wParam; - WindowMinimized = IsIconic(window); - WindowPauseLatched = !WindowActive || WindowMinimized; - UpdateWindowMinimizedPause(); + if (!(BOOL)wParam) { + WindowActive = FALSE; + WindowMinimized = IsIconic(window); + WindowPauseLatched = TRUE; + UpdateWindowMinimizedPause(); + } else { + // Do not trust the activation notification alone in fullscreen. + RefreshWindowActivityState(TRUE); + } switch ( gGameState ) { @@ -1381,10 +1390,14 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) case WM_ACTIVATE: { - WindowActive = LOWORD(wParam) != WA_INACTIVE; - WindowMinimized = HIWORD(wParam) || IsIconic(window); - WindowPauseLatched = !WindowActive || WindowMinimized; - UpdateWindowMinimizedPause(); + if (LOWORD(wParam) == WA_INACTIVE || HIWORD(wParam)) { + WindowActive = FALSE; + WindowMinimized = HIWORD(wParam) || IsIconic(window); + WindowPauseLatched = TRUE; + UpdateWindowMinimizedPause(); + } else { + RefreshWindowActivityState(TRUE); + } return 0L; } From 929078e16ef00406e73746f7d9cb85f78635d1d8 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 01:26:38 +0300 Subject: [PATCH 19/28] fix audio --- src/audio/AudioLogic.cpp | 3 ++- src/audio/DMAudio.cpp | 27 +++++++++++++++++++++-- src/audio/oal/stream.cpp | 44 +++++++++++++++++++++++++++++-------- src/audio/sampman_miles.cpp | 2 +- src/audio/sampman_oal.cpp | 5 +---- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/audio/AudioLogic.cpp b/src/audio/AudioLogic.cpp index 01e1a40..4d13ed2 100644 --- a/src/audio/AudioLogic.cpp +++ b/src/audio/AudioLogic.cpp @@ -9988,7 +9988,8 @@ cAudioManager::ProcessMissionAudioSlot(uint8 slot) break; if (m_nPreviousUserPause) { SampleManager.PauseStream(0, slot + 1); - SampleManager.StartPreloadedStreamedFile(slot + 1); + if (!SampleManager.IsStreamPlaying(slot + 1)) + SampleManager.StartPreloadedStreamedFile(slot + 1); nFramesUntilFailedResume[slot] = RESUME_RETRY_FRAMES; } if (nCheckPlayingDelay[slot]) { diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index a06c501..94fe317 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -29,8 +29,31 @@ cDMAudio::Service(void) void cDMAudio::SetStreamsPausedForWindowPause(bool paused) { - for (uint8 i = 0; i < MAX_STREAMS; i++) - SampleManager.PauseStream(paused ? 1 : 0, i); + static bool windowPauseActive = false; + static bool wasPlaying[MAX_STREAMS] = {}; + + if (paused) { + if (windowPauseActive) + return; + + windowPauseActive = true; + for (uint8 i = 0; i < MAX_STREAMS; i++) { + wasPlaying[i] = SampleManager.IsStreamPlaying(i); + if (wasPlaying[i]) + SampleManager.PauseStream(1, i); + } + return; + } + + if (!windowPauseActive) + return; + + for (uint8 i = 0; i < MAX_STREAMS; i++) { + if (wasPlaying[i]) + SampleManager.PauseStream(0, i); + wasPlaying[i] = false; + } + windowPauseActive = false; } int32 diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp index 426d0e0..bee82b9 100644 --- a/src/audio/oal/stream.cpp +++ b/src/audio/oal/stream.cpp @@ -1168,14 +1168,21 @@ uint32 CStream::GetPosMS() { if ( !HasSource() ) return 0; if ( !IsOpened() ) return 0; - - ALint offset; - //alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset); - return m_pSoundFile->Tell() - - m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels() - + m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels(); + ALint offset = 0; + ALint buffersQueued = 0; + ALint buffersProcessed = 0; + alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset); + alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued); + alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed); + + // Tell() is at the end of the decoder queue, while the source is playing + // the first pending buffer. Account for every pending buffer, not just its tail. + uint32 bufferDuration = m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples()) / m_pSoundFile->GetChannels(); + int64 position = (int64)m_pSoundFile->Tell() + - (int64)bufferDuration * Max(buffersQueued - buffersProcessed, 0) + + m_pSoundFile->samples2ms(offset / m_pSoundFile->GetSampleSize()); + return position > 0 ? (uint32)position : 0; } uint32 CStream::GetLengthMS() @@ -1301,8 +1308,27 @@ void CStream::SetPlay(bool state) void CStream::Start() { - if ( !HasSource() ) return; - if ( FillBuffers() != 0 ) + if ( !HasSource() || !IsOpened() ) return; + + if (m_bPaused) + SetPause(false); + if (IsPlaying()) + return; + + ALint buffersQueued = 0; + ALint buffersProcessed = 0; + alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued); + alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed); + if (buffersQueued > buffersProcessed) { + SetPlay(true); + return; + } + + if (buffersQueued != 0) { + SetPlay(false); + ClearBuffers(); + } + if (FillBuffers() != 0) SetPlay(true); } diff --git a/src/audio/sampman_miles.cpp b/src/audio/sampman_miles.cpp index 42ca433..f3529b7 100644 --- a/src/audio/sampman_miles.cpp +++ b/src/audio/sampman_miles.cpp @@ -2074,7 +2074,7 @@ cSampleManager::StartPreloadedStreamedFile(uint8 nStream) { if ( m_bInitialised ) { - if ( mp3Stream[nStream] ) + if ( mp3Stream[nStream] && AIL_stream_status(mp3Stream[nStream]) != SMP_PLAYING ) AIL_start_stream(mp3Stream[nStream]); } } diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp index cc40dae..d587642 100644 --- a/src/audio/sampman_oal.cpp +++ b/src/audio/sampman_oal.cpp @@ -1724,10 +1724,7 @@ cSampleManager::StartPreloadedStreamedFile(uint8 nStream) if ( stream ) { - if ( stream->IsOpened() ) - { - stream->Start(); - } + stream->Start(); } } From d92ce204d8d1b0efed3ed078c7d48468935340f6 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 01:42:28 +0300 Subject: [PATCH 20/28] last? --- src/animation/CutsceneMgr.cpp | 5 ++- src/control/Script.cpp | 9 +++-- src/core/Cam.cpp | 3 ++ src/core/Camera.cpp | 3 +- src/core/Game.cpp | 2 +- src/skel/glfw/glfw.cpp | 33 +++++++++++++++--- src/skel/win/win.cpp | 63 +++++++++++++++++++++++++++++------ 7 files changed, 97 insertions(+), 21 deletions(-) diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp index a798774..41cca81 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/animation/CutsceneMgr.cpp @@ -1,5 +1,6 @@ #include "common.h" +#include "crossplatform.h" #include "General.h" #include "CutsceneMgr.h" #include "Directory.h" @@ -557,8 +558,10 @@ CCutsceneMgr::Update(void) CUTSCENE_LOADING_4 }; - if (CTimer::GetIsWindowMinimizedPaused()) { + if (CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) { RecordWindowPause(); + CTimer::SetTimeStep(0.0f); + CTimer::SetTimeStepNonClipped(0.0f); return; } diff --git a/src/control/Script.cpp b/src/control/Script.cpp index 6e20b14..2c17b88 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -141,7 +141,7 @@ int MissionSkipLevel; static bool PauseScriptProcessingForWindowMinimized(void) { - if (!psRefreshAndGetWindowMinimizedPause()) + if (!CTimer::GetIsWindowMinimizedPaused() && !psRefreshAndGetWindowMinimizedPause()) return false; DMAudio.SetStreamsPausedForWindowPause(true); @@ -2495,7 +2495,7 @@ void CRunningScript::Process() if (m_bMissionFlag && CTheScripts::FailCurrentMission == 1 && m_nStackPointer == 1) SetIP(m_anStack[--m_nStackPointer]); if (CTimer::GetTimeInMilliseconds() >= m_nWakeTime){ - while (!PauseScriptProcessingForWindowMinimized() && !ProcessOneCommand()) + while (!ProcessOneCommand()) ; return; } @@ -2514,6 +2514,9 @@ void CRunningScript::Process() int8 CRunningScript::ProcessOneCommand() { + if (PauseScriptProcessingForWindowMinimized()) + return 1; + int8 retval = -1; ++CTheScripts::CommandsExecuted; int32 command = (uint16)CTheScripts::Read2BytesFromScript(&m_nIp); @@ -4863,7 +4866,7 @@ CTheScripts::SwitchToMission(int32 mission) if (PauseScriptProcessingForWindowMinimized()) return; - while (!PauseScriptProcessingForWindowMinimized() && !pScript->ProcessOneCommand()) + while (!pScript->ProcessOneCommand()) ; CMessages::ClearMessages(); diff --git a/src/core/Cam.cpp b/src/core/Cam.cpp index 24ce497..eaf5dc1 100644 --- a/src/core/Cam.cpp +++ b/src/core/Cam.cpp @@ -3518,6 +3518,9 @@ FindSplinePathPositionVector(CVector *out, float *spline, uint32 time, uint32 &m void CCam::Process_FlyBy(const CVector&, float, float, float) { + if (CTimer::GetIsWindowMinimizedPaused()) + return; + float UpAngle = 0.0f; static float FirstFOVValue = 0.0f; static float PsuedoFOV; diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp index 63b9f76..7281e18 100644 --- a/src/core/Camera.cpp +++ b/src/core/Camera.cpp @@ -1,5 +1,6 @@ #include "common.h" +#include "crossplatform.h" #include "main.h" #include "Draw.h" #include "World.h" @@ -253,7 +254,7 @@ CCamera::Init(void) void CCamera::Process(void) { - if (CTimer::GetIsWindowMinimizedPaused()) + if (CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) return; // static bool InterpolatorNotInitialised = true; // unused diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 5a7dac8..0fd0639 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -828,7 +828,7 @@ void CGame::InitialiseWhenRestarting(void) void CGame::Process(void) { - if (psRefreshAndGetWindowMinimizedPause() || CTimer::GetIsWindowMinimizedPaused()) { + if (CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) { DMAudio.SetStreamsPausedForWindowPause(true); DMAudio.Service(); return; diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 54e2b55..fb71269 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -76,6 +76,7 @@ static RwBool ForegroundApp = TRUE; static RwBool WindowIconified = FALSE; static RwBool WindowFocused = TRUE; static RwBool WindowPauseLatched = FALSE; +static uint8 WindowActiveFrames = 0; static RwBool WindowAudioSuspended = FALSE; static RwBool RwInitialised = FALSE; @@ -105,6 +106,19 @@ IsWindowMinimizedPauseActive(void) return WindowPauseLatched || WindowIconified || !WindowFocused; } +#ifdef _WIN32 +static RwBool +IsNativeWindowMinimized(HWND window) +{ + if (window == nil) + return TRUE; + + WINDOWPLACEMENT placement = {}; + placement.length = sizeof(placement); + return IsIconic(window) || (GetWindowPlacement(window, &placement) && placement.showCmd == SW_SHOWMINIMIZED); +} +#endif + static void ReadWindowActivityState(void) { @@ -120,7 +134,7 @@ ReadWindowActivityState(void) // minimized. The native foreground window is authoritative on Windows. HWND nativeWindow = glfwGetWin32Window(window); if (nativeWindow != nil) { - WindowIconified = IsIconic(nativeWindow); + WindowIconified = IsNativeWindowMinimized(nativeWindow); WindowFocused = !WindowIconified && GetForegroundWindow() == nativeWindow; } #endif @@ -185,10 +199,17 @@ static RwBool RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) { ReadWindowActivityState(); - if (WindowIconified || !WindowFocused) + if (WindowIconified || !WindowFocused) { WindowPauseLatched = TRUE; - else if (allowClearPauseLatch) - WindowPauseLatched = FALSE; + WindowActiveFrames = 0; + } else if (allowClearPauseLatch && WindowPauseLatched) { + if (++WindowActiveFrames >= 2) { + WindowPauseLatched = FALSE; + WindowActiveFrames = 0; + } + } else { + WindowActiveFrames = 0; + } UpdateWindowMinimizedPause(); return IsWindowMinimizedPauseActive(); } @@ -213,6 +234,7 @@ psRestoreFocusAfterPause(void) WindowPauseLatched = FALSE; else WindowPauseLatched = TRUE; + WindowActiveFrames = 0; if (IsWindowMinimizedPauseActive()) { UpdateWindowMinimizedPause(); @@ -473,6 +495,7 @@ psInitialize(void) WindowFocused = TRUE; WindowIconified = FALSE; WindowPauseLatched = FALSE; + WindowActiveFrames = 0; WindowAudioSuspended = FALSE; PsGlobal.joy1id = -1; @@ -1977,6 +2000,7 @@ windowFocusCB(GLFWwindow* window, int focused) { if (!focused) { WindowFocused = FALSE; WindowPauseLatched = TRUE; + WindowActiveFrames = 0; UpdateWindowMinimizedPause(); return; } @@ -1989,6 +2013,7 @@ windowIconifyCB(GLFWwindow* window, int iconified) { if (iconified) { WindowIconified = TRUE; WindowPauseLatched = TRUE; + WindowActiveFrames = 0; UpdateWindowMinimizedPause(); return; } diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index b44063a..f0cb7eb 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -58,6 +58,7 @@ static RwBool ForegroundApp = TRUE; static RwBool WindowMinimized = FALSE; static RwBool WindowActive = TRUE; static RwBool WindowPauseLatched = FALSE; +static uint8 WindowActiveFrames = 0; static RwBool WindowAudioSuspended = FALSE; static RwBool RwInitialised = FALSE; @@ -149,12 +150,23 @@ IsWindowMinimizedPauseActive(void) return WindowPauseLatched || WindowMinimized || !WindowActive; } +static RwBool +IsNativeWindowMinimized(HWND window) +{ + if (window == nil) + return TRUE; + + WINDOWPLACEMENT placement = {}; + placement.length = sizeof(placement); + return IsIconic(window) || (GetWindowPlacement(window, &placement) && placement.showCmd == SW_SHOWMINIMIZED); +} + static void ReadWindowActivityState(void) { HWND currentWindow = PSGLOBAL(window); - WindowMinimized = IsIconic(currentWindow); - WindowActive = !WindowMinimized && GetForegroundWindow() == currentWindow; + WindowMinimized = IsNativeWindowMinimized(currentWindow); + WindowActive = currentWindow != nil && !WindowMinimized && GetForegroundWindow() == currentWindow; } static RwBool @@ -191,10 +203,17 @@ static RwBool RefreshWindowActivityState(RwBool allowClearPauseLatch = FALSE) { ReadWindowActivityState(); - if (WindowMinimized || !WindowActive) + if (WindowMinimized || !WindowActive) { WindowPauseLatched = TRUE; - else if (allowClearPauseLatch) - WindowPauseLatched = FALSE; + WindowActiveFrames = 0; + } else if (allowClearPauseLatch && WindowPauseLatched) { + if (++WindowActiveFrames >= 2) { + WindowPauseLatched = FALSE; + WindowActiveFrames = 0; + } + } else { + WindowActiveFrames = 0; + } UpdateWindowMinimizedPause(); return IsWindowMinimizedPauseActive(); } @@ -219,6 +238,7 @@ psRestoreFocusAfterPause(void) WindowPauseLatched = FALSE; else WindowPauseLatched = TRUE; + WindowActiveFrames = 0; if (IsWindowMinimizedPauseActive()) { UpdateWindowMinimizedPause(); @@ -767,6 +787,7 @@ psInitialize(void) WindowMinimized = FALSE; WindowActive = TRUE; WindowPauseLatched = FALSE; + WindowActiveFrames = 0; WindowAudioSuspended = FALSE; PsGlobal.dinterface = nil; @@ -1119,13 +1140,29 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) break; // is this correct ? } + + case WM_SYSCOMMAND: + { + if ((wParam & 0xFFF0) == SC_MINIMIZE) { + // In exclusive fullscreen, WM_SIZE can arrive after the game has + // already run another frame. Latch the pause before Windows minimizes it. + WindowMinimized = TRUE; + WindowActive = FALSE; + WindowPauseLatched = TRUE; + WindowActiveFrames = 0; + UpdateWindowMinimizedPause(); + } + break; + } case WM_SIZE: { RwRect r; - WindowMinimized = wParam == SIZE_MINIMIZED || IsIconic(window); - if (WindowMinimized) + WindowMinimized = wParam == SIZE_MINIMIZED || IsNativeWindowMinimized(window); + if (WindowMinimized) { WindowPauseLatched = TRUE; + WindowActiveFrames = 0; + } UpdateWindowMinimizedPause(); r.x = 0; @@ -1322,8 +1359,9 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { if (!(BOOL)wParam) { WindowActive = FALSE; - WindowMinimized = IsIconic(window); + WindowMinimized = IsNativeWindowMinimized(window); WindowPauseLatched = TRUE; + WindowActiveFrames = 0; UpdateWindowMinimizedPause(); } else { // Do not trust the activation notification alone in fullscreen. @@ -1392,8 +1430,9 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { if (LOWORD(wParam) == WA_INACTIVE || HIWORD(wParam)) { WindowActive = FALSE; - WindowMinimized = HIWORD(wParam) || IsIconic(window); + WindowMinimized = HIWORD(wParam) || IsNativeWindowMinimized(window); WindowPauseLatched = TRUE; + WindowActiveFrames = 0; UpdateWindowMinimizedPause(); } else { RefreshWindowActivityState(TRUE); @@ -1485,11 +1524,13 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) #ifdef FIX_BUGS WindowActive = FALSE; WindowPauseLatched = TRUE; + WindowActiveFrames = 0; #else WindowActive = TRUE; - WindowPauseLatched = IsIconic(window); + WindowPauseLatched = IsNativeWindowMinimized(window); + WindowActiveFrames = 0; #endif - WindowMinimized = IsIconic(window); + WindowMinimized = IsNativeWindowMinimized(window); UpdateWindowMinimizedPause(); break; } From 8a20efe95701f3b3953e30ae29fe8b5289fa82c7 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 12:01:37 +0300 Subject: [PATCH 21/28] . --- src/animation/CutsceneMgr.cpp | 3 +- src/control/Script.cpp | 3 +- src/core/Cam.cpp | 3 +- src/core/Camera.cpp | 3 +- src/core/Frontend.cpp | 10 +++-- src/core/Game.cpp | 72 ++++++++++++++++++++++++++--------- src/core/Game.h | 4 +- src/core/World.cpp | 3 +- src/core/main.cpp | 2 + src/skel/glfw/glfw.cpp | 19 ++++----- src/skel/win/win.cpp | 16 +++----- 11 files changed, 88 insertions(+), 50 deletions(-) diff --git a/src/animation/CutsceneMgr.cpp b/src/animation/CutsceneMgr.cpp index 41cca81..4cab79e 100644 --- a/src/animation/CutsceneMgr.cpp +++ b/src/animation/CutsceneMgr.cpp @@ -3,6 +3,7 @@ #include "crossplatform.h" #include "General.h" #include "CutsceneMgr.h" +#include "Game.h" #include "Directory.h" #include "Camera.h" #include "Streaming.h" @@ -558,7 +559,7 @@ CCutsceneMgr::Update(void) CUTSCENE_LOADING_4 }; - if (CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) { + if (CGame::IsWindowPauseMenuActive() || CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) { RecordWindowPause(); CTimer::SetTimeStep(0.0f); CTimer::SetTimeStepNonClipped(0.0f); diff --git a/src/control/Script.cpp b/src/control/Script.cpp index 2c17b88..90fbc9d 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -42,6 +42,7 @@ #include "Ropes.h" #include "ColStore.h" #include "Fluff.h" +#include "Game.h" #include "GameLogic.h" #include "MBlur.h" #include "PedRoutes.h" @@ -141,7 +142,7 @@ int MissionSkipLevel; static bool PauseScriptProcessingForWindowMinimized(void) { - if (!CTimer::GetIsWindowMinimizedPaused() && !psRefreshAndGetWindowMinimizedPause()) + if (!CGame::IsWindowPauseMenuActive() && !CTimer::GetIsWindowMinimizedPaused() && !psRefreshAndGetWindowMinimizedPause()) return false; DMAudio.SetStreamsPausedForWindowPause(true); diff --git a/src/core/Cam.cpp b/src/core/Cam.cpp index eaf5dc1..dda97cd 100644 --- a/src/core/Cam.cpp +++ b/src/core/Cam.cpp @@ -15,6 +15,7 @@ #include "Pad.h" #include "Frontend.h" #include "General.h" +#include "Game.h" #include "Timecycle.h" #include "Renderer.h" #include "Shadows.h" @@ -3518,7 +3519,7 @@ FindSplinePathPositionVector(CVector *out, float *spline, uint32 time, uint32 &m void CCam::Process_FlyBy(const CVector&, float, float, float) { - if (CTimer::GetIsWindowMinimizedPaused()) + if (CGame::IsWindowPauseMenuActive() || CTimer::GetIsWindowMinimizedPaused()) return; float UpAngle = 0.0f; diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp index 7281e18..5b057b8 100644 --- a/src/core/Camera.cpp +++ b/src/core/Camera.cpp @@ -3,6 +3,7 @@ #include "crossplatform.h" #include "main.h" #include "Draw.h" +#include "Game.h" #include "World.h" #include "Vehicle.h" #include "Train.h" @@ -254,7 +255,7 @@ CCamera::Init(void) void CCamera::Process(void) { - if (CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) + if (CGame::IsWindowPauseMenuActive() || CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) return; // static bool InterpolatorNotInitialised = true; // unused diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index cc89d21..a649a42 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -573,7 +573,8 @@ CMenuManager::Initialise(void) CPad::StopPadsShaking(); if (!m_OnlySaveMenu) m_nCurrScreen = MENUPAGE_NONE; - DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); + if (!CGame::IsWindowPauseMenuActive()) + DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); DMAudio.Service(); DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); @@ -3439,7 +3440,7 @@ CMenuManager::Process(void) ProcessDialogTimer(); #endif - if (TheCamera.GetScreenFadeStatus() != FADE_0) + if (TheCamera.GetScreenFadeStatus() != FADE_0 && !CGame::IsWindowPauseMenuActive()) return; InitialiseChangedLanguageSettings(); @@ -5462,7 +5463,7 @@ CMenuManager::ProcessFileActions() void CMenuManager::SwitchMenuOnAndOff() { - if (!TheCamera.m_WideScreenOn) { + if (!TheCamera.m_WideScreenOn || CGame::IsWindowPauseMenuActive()) { // Reminder: You need REGISTER_START_BUTTON defined to make it work. if ((CPad::GetPad(0)->GetStartJustDown() || CPad::GetPad(0)->GetEscapeJustDown()) @@ -5502,6 +5503,7 @@ CMenuManager::SwitchMenuOnAndOff() CPad::StopPadsShaking(); #endif } else { + bool wasWindowPauseMenu = CGame::IsWindowPauseMenuActive(); psRestoreFocusAfterPause(); #ifdef EXTENDED_COLOURFILTER // we always expect CPostFX to be open @@ -5549,6 +5551,8 @@ CMenuManager::SwitchMenuOnAndOff() #endif UnloadTextures(); CTimer::EndUserPause(); + if (wasWindowPauseMenu) + CGame::FinishWindowPauseMenu(); CTimer::Update(); m_OnlySaveMenu = false; } diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 0fd0639..a48de5c 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -124,6 +124,12 @@ bool8 CGame::VarUpdatePlayerCoords; int gameTxdSlot; +// This survives a transient fullscreen focus report until the player explicitly +// leaves the pause menu. It must not live in CMenuManager: that class has a +// fixed binary layout on the original PC menu path. +static bool WindowPauseMenuActive = false; +static bool WindowPauseStreamsNeedResume = false; + #ifdef SECUROM uint8 gameProcessPirateCheck = 0; #endif @@ -345,7 +351,9 @@ bool CGame::InitialiseOnceAfterRW(void) void CGame::FinalShutdown(void) -{ +{ + WindowPauseMenuActive = false; + WindowPauseStreamsNeedResume = false; CTxdStore::Shutdown(); CPedStats::Shutdown(); CdStreamShutdown(); @@ -353,6 +361,8 @@ CGame::FinalShutdown(void) bool CGame::Initialise(const char* datFile) { + WindowPauseMenuActive = false; + WindowPauseStreamsNeedResume = false; ResetLoadingScreenBar(); strcpy(aDatFile, datFile); @@ -540,7 +550,7 @@ bool CGame::Initialise(const char* datFile) #endif { CTheScripts::StartTestScript(); - if (!psRefreshAndGetWindowMinimizedPause()) { + if (!IsWindowPauseMenuActive() && !psRefreshAndGetWindowMinimizedPause()) { CTheScripts::Process(); TheCamera.Process(); } @@ -702,7 +712,7 @@ void CGame::ReInitGameObjectVariables(void) { CCranes::InitCranes(); CTheScripts::StartTestScript(); - if (!psRefreshAndGetWindowMinimizedPause()) { + if (!IsWindowPauseMenuActive() && !psRefreshAndGetWindowMinimizedPause()) { CTheScripts::Process(); TheCamera.Process(); } @@ -750,6 +760,8 @@ void CGame::ShutDownForRestart(void) void CGame::InitialiseWhenRestarting(void) { + WindowPauseMenuActive = false; + WindowPauseStreamsNeedResume = false; CRect rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); CRGBA color(255, 255, 255, 255); @@ -835,6 +847,14 @@ void CGame::Process(void) } CPad::UpdatePads(); + if (IsWindowPauseMenuActive()) { + CTimer::StartUserPause(); + DMAudio.SetStreamsPausedForWindowPause(true); + DMAudio.Service(); + FrontEndMenuManager.Process(); + return; + } + #ifdef USE_CUSTOM_ALLOCATOR ProcessTidyUpMemory(); #endif @@ -1272,28 +1292,44 @@ void CGame::ProcessTidyUpMemory(void) void CGame::InitAfterFocusLoss() { - if (ShouldUseBackgroundPauseAfterFocusLoss()) + if (WindowPauseMenuActive || FrontEndMenuManager.m_bGameNotLoaded || FrontEndMenuManager.m_bMenuActive) return; + WindowPauseMenuActive = true; FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = FrontEndMenuManager.m_lastWorking3DAudioProvider; - DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_lastWorking3DAudioProvider); - - if (!FrontEndMenuManager.m_bGameNotLoaded && !FrontEndMenuManager.m_bMenuActive) { - CTimer::StartUserPause(); - FrontEndMenuManager.m_bStartUpFrontEndRequested = true; - } + if (DMAudio.GetCurrent3DProviderIndex() != FrontEndMenuManager.m_lastWorking3DAudioProvider) + DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_lastWorking3DAudioProvider); + CTimer::StartUserPause(); + FrontEndMenuManager.m_bStartUpFrontEndRequested = true; } bool -CGame::ShouldUseBackgroundPauseAfterFocusLoss() +CGame::IsWindowPauseMenuActive() { - return CGame::playingIntro - || CCutsceneMgr::IsActiveOrLoadingForPause() - || CWorld::bProcessCutsceneOnly - || TheCamera.m_WideScreenOn - || CTheScripts::UseTextCommands != 0 - || CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_CUTSCENE) - || CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PHONE); + return WindowPauseMenuActive; +} + +void +CGame::FinishWindowPauseMenu() +{ + if (!WindowPauseMenuActive) + return; + + WindowPauseMenuActive = false; + WindowPauseStreamsNeedResume = true; + CTimer::StartUserPause(); +} + +void +CGame::ResumeWindowPauseMenuAfterFocusRestore() +{ + if (WindowPauseMenuActive || !WindowPauseStreamsNeedResume || CTimer::GetIsWindowMinimizedPaused()) + return; + + CTimer::EndUserPause(); + DMAudio.SetStreamsPausedForWindowPause(false); + DMAudio.Service(); + WindowPauseStreamsNeedResume = false; } bool diff --git a/src/core/Game.h b/src/core/Game.h index 8472a86..53d8a24 100644 --- a/src/core/Game.h +++ b/src/core/Game.h @@ -68,7 +68,9 @@ class CGame static void Process(void); static void InitAfterFocusLoss(void); - static bool ShouldUseBackgroundPauseAfterFocusLoss(void); + static bool IsWindowPauseMenuActive(void); + static void FinishWindowPauseMenu(void); + static void ResumeWindowPauseMenuAfterFocusRestore(void); static bool IsInInterior(void) { return currArea != AREA_MAIN_MAP; } static bool CanSeeWaterFromCurrArea(void); diff --git a/src/core/World.cpp b/src/core/World.cpp index d4bdfcc..84c60b6 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -7,6 +7,7 @@ #include "EventList.h" #include "Explosion.h" #include "Fire.h" +#include "Game.h" #include "Garages.h" #include "Glass.h" #include "Messages.h" @@ -1921,7 +1922,7 @@ CWorld::RemoveStaticObjects() void CWorld::Process(void) { - if (CTimer::GetIsWindowMinimizedPaused()) + if (CGame::IsWindowPauseMenuActive() || CTimer::GetIsWindowMinimizedPaused()) return; if(!(CTimer::GetFrameCounter() & 63)) CReferences::PruneAllReferencesInWorld(); diff --git a/src/core/main.cpp b/src/core/main.cpp index da1b505..42f595e 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -1471,6 +1471,8 @@ void Idle(void *arg) { bool windowPause = psRefreshAndGetWindowMinimizedPause(); + if (!windowPause) + CGame::ResumeWindowPauseMenuAfterFocusRestore(); CTimer::Update(); if (windowPause || CTimer::GetIsWindowMinimizedPaused()) { diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index fb71269..35570ad 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -140,12 +140,6 @@ ReadWindowActivityState(void) #endif } -static RwBool -ShouldUseBackgroundPauseAfterFocusLoss(void) -{ - return CGame::ShouldUseBackgroundPauseAfterFocusLoss(); -} - static void SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) { @@ -163,10 +157,14 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) } else { - _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); + if (CGame::IsWindowPauseMenuActive()) + _InputInitialiseMouse(false); + else + _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); - DMAudio.SetStreamsPausedForWindowPause(false); + if (!CGame::IsWindowPauseMenuActive()) + DMAudio.SetStreamsPausedForWindowPause(false); DMAudio.Service(); } } @@ -175,7 +173,6 @@ static void UpdateWindowMinimizedPause(void) { RwBool windowPause = IsWindowMinimizedPauseActive(); - RwBool suppressPauseMenu = ShouldUseBackgroundPauseAfterFocusLoss(); RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); if (!windowPause) { @@ -186,10 +183,8 @@ UpdateWindowMinimizedPause(void) return; } - if (ForegroundApp && !suppressPauseMenu) - CGame::InitAfterFocusLoss(); - CTimer::SetWindowMinimizedPause(true); + CGame::InitAfterFocusLoss(); CCutsceneMgr::RecordWindowPause(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index f0cb7eb..e91b070 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -127,9 +127,12 @@ SetWindowAudioSuspended(RwBool suspend, RwBool force = FALSE) } else { + if (CGame::IsWindowPauseMenuActive() && PSGLOBAL(mouse) != nil) + PSGLOBAL(mouse)->Acquire(); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); - DMAudio.SetStreamsPausedForWindowPause(false); + if (!CGame::IsWindowPauseMenuActive()) + DMAudio.SetStreamsPausedForWindowPause(false); DMAudio.Service(); } } @@ -169,17 +172,10 @@ ReadWindowActivityState(void) WindowActive = currentWindow != nil && !WindowMinimized && GetForegroundWindow() == currentWindow; } -static RwBool -ShouldUseBackgroundPauseAfterFocusLoss(void) -{ - return CGame::ShouldUseBackgroundPauseAfterFocusLoss(); -} - static void UpdateWindowMinimizedPause(void) { RwBool windowPause = IsWindowMinimizedPauseActive(); - RwBool suppressPauseMenu = ShouldUseBackgroundPauseAfterFocusLoss(); RwBool wasWindowPause = CTimer::GetIsWindowMinimizedPaused(); if (!windowPause) { @@ -190,10 +186,8 @@ UpdateWindowMinimizedPause(void) return; } - if (ForegroundApp && !suppressPauseMenu) - CGame::InitAfterFocusLoss(); - CTimer::SetWindowMinimizedPause(true); + CGame::InitAfterFocusLoss(); CCutsceneMgr::RecordWindowPause(); ForegroundApp = FALSE; SetWindowAudioSuspended(TRUE); From 7063c1bcb94f9c6469217c773f2292f56d114b19 Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 12:13:33 +0300 Subject: [PATCH 22/28] Update Game.cpp --- src/core/Game.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/Game.cpp b/src/core/Game.cpp index a48de5c..70ae01d 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -1318,14 +1318,19 @@ CGame::FinishWindowPauseMenu() WindowPauseMenuActive = false; WindowPauseStreamsNeedResume = true; CTimer::StartUserPause(); + ResumeWindowPauseMenuAfterFocusRestore(); } void CGame::ResumeWindowPauseMenuAfterFocusRestore() { - if (WindowPauseMenuActive || !WindowPauseStreamsNeedResume || CTimer::GetIsWindowMinimizedPaused()) + if (WindowPauseMenuActive || !WindowPauseStreamsNeedResume || CTimer::GetIsWindowMinimizedPaused() || psRefreshAndGetWindowMinimizedPause()) return; + // A focus-loss pause mutes the backend. Restore the user-selected masters + // here as well, since Continue is the only point that releases its streams. + DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); + DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); CTimer::EndUserPause(); DMAudio.SetStreamsPausedForWindowPause(false); DMAudio.Service(); From aae4151e9f52abda16db05a8849674b07d073c2d Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 12:27:15 +0300 Subject: [PATCH 23/28] Update Frontend.cpp --- src/core/Frontend.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index a649a42..9963268 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -5605,7 +5605,10 @@ CMenuManager::UnloadTextures() DMAudio.StopFrontEndTrack(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); - DMAudio.ChangeMusicMode(MUSICMODE_GAME); + // A forced focus-loss menu can overlay a cutscene. Keep its music mode so + // stream 0 remains preloaded and can resume when Continue closes the menu. + if (!CGame::IsWindowPauseMenuActive()) + DMAudio.ChangeMusicMode(MUSICMODE_GAME); if (m_bSpritesLoaded) { printf("REMOVE frontend\n"); int frontend = CTxdStore::FindTxdSlot("frontend1"); From 5a9be7b222576ece22d8ab6ff44ad8740adc543c Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 19:15:21 +0300 Subject: [PATCH 24/28] fix screen mode --- src/core/MenuScreensCustom.cpp | 2 ++ src/skel/glfw/glfw.cpp | 39 +++++++++++++++++++-- src/skel/win/win.cpp | 64 ++++++++++++++++++++++++++++------ 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp index 0a47553..98cca4b 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/core/MenuScreensCustom.cpp @@ -256,6 +256,8 @@ const char* screenModes[] = { "FED_FLS", "FED_WND", "FED_BDL" }; void ScreenModeAfterChange(int8 before, int8 after) { _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); // apply same resolution + CMenuManager::CentreMousePointer(); + FrontEndMenuManager.m_bShowMouse = true; FrontEndMenuManager.SetHelperText(0); } diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 35570ad..6ff62e0 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -769,8 +769,8 @@ RwBool IsForegroundApp() #ifdef IMPROVED_VIDEOMODE -static void -GetBorderlessMonitorInfo(int *x, int *y, int *width, int *height) +static GLFWmonitor * +GetCurrentMonitor(void) { int numMonitors; GLFWmonitor **monitors = glfwGetMonitors(&numMonitors); @@ -780,6 +780,13 @@ GetBorderlessMonitorInfo(int *x, int *y, int *width, int *height) monitor = monitors[GcurSel]; if (monitor == nil) monitor = glfwGetPrimaryMonitor(); + return monitor; +} + +static void +GetBorderlessMonitorInfo(int *x, int *y, int *width, int *height) +{ + GLFWmonitor *monitor = GetCurrentMonitor(); if (monitor != nil) { const GLFWvidmode *mode = glfwGetVideoMode(monitor); @@ -797,6 +804,22 @@ GetBorderlessMonitorInfo(int *x, int *y, int *width, int *height) *width = FrontEndMenuManager.m_nPrefsWidth; *height = FrontEndMenuManager.m_nPrefsHeight; } + +static void +GetWindowedMonitorWorkArea(int *x, int *y, int *width, int *height) +{ + GLFWmonitor *monitor = GetCurrentMonitor(); + if (monitor != nil) { + glfwGetMonitorWorkarea(monitor, x, y, width, height); + if (*width > 0 && *height > 0) + return; + } + + *x = 0; + *y = 0; + *width = FrontEndMenuManager.m_nPrefsWidth; + *height = FrontEndMenuManager.m_nPrefsHeight; +} #endif /* @@ -1205,6 +1228,18 @@ void psPostRWinit(void) } else { glfwSetWindowAttrib(PSGLOBAL(window), GLFW_DECORATED, GLFW_TRUE); glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); + + int workX, workY, workWidth, workHeight; + int frameLeft, frameTop, frameRight, frameBottom; + GetWindowedMonitorWorkArea(&workX, &workY, &workWidth, &workHeight); + glfwGetWindowFrameSize(PSGLOBAL(window), &frameLeft, &frameTop, &frameRight, &frameBottom); + + int windowWidth = RsGlobal.maximumWidth + frameLeft + frameRight; + int windowHeight = RsGlobal.maximumHeight + frameTop + frameBottom; + int spaceX = workWidth - windowWidth; + int spaceY = workHeight - windowHeight; + glfwSetWindowPos(PSGLOBAL(window), workX + (spaceX > 0 ? spaceX / 2 : 0), + workY + (spaceY > 0 ? spaceY / 2 : 0)); } #else glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index e91b070..aac3d94 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -101,11 +101,28 @@ static psGlobalType PsGlobal; #include "AnimViewer.h" #include "MemoryMgr.h" +static RwBool NativeCursorHidden = FALSE; + +static void +SetNativeCursorHidden(RwBool hidden) +{ + if (NativeCursorHidden == hidden) + return; + + if (hidden) { + while (ShowCursor(FALSE) >= 0) {} + } else { + while (ShowCursor(TRUE) < 0) {} + } + NativeCursorHidden = hidden; +} + static void ReleaseWindowMouseFocus(void) { ReleaseCapture(); ClipCursor(nil); + SetNativeCursorHidden(FALSE); if (PSGLOBAL(mouse) != nil) PSGLOBAL(mouse)->Unacquire(); } @@ -1128,11 +1145,14 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { case WM_SETCURSOR: { - ShowCursor(FALSE); - + if (LOWORD(lParam) != HTCLIENT) { + SetNativeCursorHidden(FALSE); + return DefWindowProc(window, message, wParam, lParam); + } + + SetNativeCursorHidden(TRUE); SetCursor(nil); - - break; // is this correct ? + return TRUE; } case WM_SYSCOMMAND: @@ -1645,6 +1665,26 @@ GetBorderlessMonitorRect(RECT *rect) rect->bottom = GetSystemMetrics(SM_CYSCREEN); } } + +static void +GetWindowedMonitorWorkArea(RECT *rect) +{ + HMONITOR monitor = MonitorFromWindow(PSGLOBAL(window), MONITOR_DEFAULTTONEAREST); + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(monitorInfo); + + if (monitor != nil && GetMonitorInfo(monitor, &monitorInfo)) { + *rect = monitorInfo.rcWork; + return; + } + + if (!SystemParametersInfo(SPI_GETWORKAREA, 0, rect, 0)) { + rect->left = 0; + rect->top = 0; + rect->right = GetSystemMetrics(SM_CXSCREEN); + rect->bottom = GetSystemMetrics(SM_CYSCREEN); + } +} #endif /* @@ -1866,14 +1906,18 @@ psSelectDevice() rect.bottom = FrontEndMenuManager.m_nPrefsHeight; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); - // center it - int spaceX = GetSystemMetrics(SM_CXSCREEN) - (rect.right-rect.left); - int spaceY = GetSystemMetrics(SM_CYSCREEN) - (rect.bottom-rect.top); + RECT workArea; + GetWindowedMonitorWorkArea(&workArea); + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + int spaceX = (workArea.right - workArea.left) - width; + int spaceY = (workArea.bottom - workArea.top) - height; + int x = workArea.left + (spaceX > 0 ? spaceX / 2 : 0); + int y = workArea.top + (spaceY > 0 ? spaceY / 2 : 0); SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); - SetWindowPos(PSGLOBAL(window), HWND_NOTOPMOST, spaceX/2, spaceY/2, - (rect.right - rect.left), - (rect.bottom - rect.top), 0); + SetWindowPos(PSGLOBAL(window), HWND_NOTOPMOST, x, y, width, height, + SWP_FRAMECHANGED | SWP_SHOWWINDOW); // Have to get actual size because the window perhaps didn't fit GetClientRect(PSGLOBAL(window), &rect); From 8c506def3f5da9997e0000ef84aca2ab46bb09bb Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 19:59:44 +0300 Subject: [PATCH 25/28] ble --- src/skel/glfw/glfw.cpp | 1 + src/skel/win/win.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 6ff62e0..d97f963 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -1240,6 +1240,7 @@ void psPostRWinit(void) int spaceY = workHeight - windowHeight; glfwSetWindowPos(PSGLOBAL(window), workX + (spaceX > 0 ? spaceX / 2 : 0), workY + (spaceY > 0 ? spaceY / 2 : 0)); + glfwMaximizeWindow(PSGLOBAL(window)); } #else glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index aac3d94..4725e80 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -1918,6 +1918,7 @@ psSelectDevice() SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); SetWindowPos(PSGLOBAL(window), HWND_NOTOPMOST, x, y, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW); + ShowWindow(PSGLOBAL(window), SW_MAXIMIZE); // Have to get actual size because the window perhaps didn't fit GetClientRect(PSGLOBAL(window), &rect); @@ -2447,7 +2448,12 @@ WinMain(HINSTANCE instance, /* * Show the window, and refresh it... */ - ShowWindow(PSGLOBAL(window), cmdShow); +#ifdef IMPROVED_VIDEOMODE + if (FrontEndMenuManager.m_nPrefsWindowed == WINDOWMODE_WINDOWED) + ShowWindow(PSGLOBAL(window), SW_MAXIMIZE); + else +#endif + ShowWindow(PSGLOBAL(window), cmdShow); UpdateWindow(PSGLOBAL(window)); { From 0849280ff6c80a938ad82bcba5186ee327d771ab Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 20:15:43 +0300 Subject: [PATCH 26/28] Update MenuScreensCustom.cpp --- src/core/MenuScreensCustom.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp index 98cca4b..2309a58 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/core/MenuScreensCustom.cpp @@ -255,9 +255,19 @@ wchar* MultiSamplingDraw(bool *disabled, bool userHovering) { const char* screenModes[] = { "FED_FLS", "FED_WND", "FED_BDL" }; void ScreenModeAfterChange(int8 before, int8 after) { + bool useMouse = FrontEndMenuManager.m_bShowMouse; _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); // apply same resolution CMenuManager::CentreMousePointer(); - FrontEndMenuManager.m_bShowMouse = true; + // Ignore the synthetic mouse move caused by recreating the video mode. + int32 mouseX = SCREEN_WIDTH / 2; + int32 mouseY = SCREEN_HEIGHT / 2; + FrontEndMenuManager.m_nMouseTempPosX = mouseX; + FrontEndMenuManager.m_nMouseTempPosY = mouseY; + FrontEndMenuManager.m_nMousePosX = mouseX; + FrontEndMenuManager.m_nMousePosY = mouseY; + FrontEndMenuManager.m_nMouseOldPosX = mouseX; + FrontEndMenuManager.m_nMouseOldPosY = mouseY; + FrontEndMenuManager.m_bShowMouse = useMouse; FrontEndMenuManager.SetHelperText(0); } From aff86c0c71c11ed671c67255fd901766ee4058eb Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 20:24:46 +0300 Subject: [PATCH 27/28] Update MenuScreensCustom.cpp --- src/core/MenuScreensCustom.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp index 2309a58..27c9d50 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/core/MenuScreensCustom.cpp @@ -256,6 +256,7 @@ const char* screenModes[] = { "FED_FLS", "FED_WND", "FED_BDL" }; void ScreenModeAfterChange(int8 before, int8 after) { bool useMouse = FrontEndMenuManager.m_bShowMouse; + int32 selectedOption = FrontEndMenuManager.m_nCurrOption; _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); // apply same resolution CMenuManager::CentreMousePointer(); // Ignore the synthetic mouse move caused by recreating the video mode. @@ -268,6 +269,8 @@ void ScreenModeAfterChange(int8 before, int8 after) FrontEndMenuManager.m_nMouseOldPosX = mouseX; FrontEndMenuManager.m_nMouseOldPosY = mouseY; FrontEndMenuManager.m_bShowMouse = useMouse; + // psSelectDevice resets this while recreating the RenderWare video mode. + FrontEndMenuManager.m_nCurrOption = selectedOption; FrontEndMenuManager.SetHelperText(0); } From a03114fc58a280192e5f7fdbf0bcdca3fb688c2d Mon Sep 17 00:00:00 2001 From: tutbylya Date: Fri, 10 Jul 2026 20:56:08 +0300 Subject: [PATCH 28/28] fix bug switching screen --- src/core/MenuScreensCustom.cpp | 3 +++ src/skel/glfw/glfw.cpp | 3 +++ src/skel/win/win.cpp | 13 +++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp index 27c9d50..68e6f26 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/core/MenuScreensCustom.cpp @@ -258,6 +258,9 @@ void ScreenModeAfterChange(int8 before, int8 after) bool useMouse = FrontEndMenuManager.m_bShowMouse; int32 selectedOption = FrontEndMenuManager.m_nCurrOption; _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); // apply same resolution + // Recreating the video mode can emit a synthetic focus transition even + // though the settings menu is still the foreground window. + psRestoreFocusAfterPause(); CMenuManager::CentreMousePointer(); // Ignore the synthetic mouse move caused by recreating the video mode. int32 mouseX = SCREEN_WIDTH / 2; diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index d97f963..4e0e30a 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -1218,6 +1218,9 @@ void psPostRWinit(void) if(!(vm.flags & rwVIDEOMODEEXCLUSIVE)) { #ifdef IMPROVED_VIDEOMODE + // Do not retain an always-on-top state after switching away from + // exclusive fullscreen on platforms which support floating windows. + glfwSetWindowAttrib(PSGLOBAL(window), GLFW_FLOATING, GLFW_FALSE); if (FrontEndMenuManager.m_nPrefsWindowed == WINDOWMODE_BORDERLESS) { int x, y, width, height; GetBorderlessMonitorInfo(&x, &y, &width, &height); diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index 4725e80..7b0031b 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -1238,7 +1238,7 @@ MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) rect.bottom = newPos->bottom - newPos->top; rect.right = newPos->right - newPos->left; - SetWindowPos(window, HWND_TOP, rect.left, rect.top, + SetWindowPos(window, HWND_NOTOPMOST, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top), SWP_NOMOVE); @@ -1894,7 +1894,10 @@ psSelectDevice() int height = rect.bottom - rect.top; SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_VISIBLE | WS_POPUP); - SetWindowPos(PSGLOBAL(window), HWND_TOP, rect.left, rect.top, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW); + // Leaving exclusive fullscreen can leave this window in the topmost + // z-order group. Borderless must stay a normal, switchable window. + SetWindowPos(PSGLOBAL(window), HWND_NOTOPMOST, rect.left, rect.top, width, height, + SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE); RsGlobal.maximumWidth = width; RsGlobal.maximumHeight = height; @@ -2817,6 +2820,12 @@ WinMain(HINSTANCE instance, ForegroundApp = TRUE; SetWindowAudioSuspended(FALSE, TRUE); } + else if (WindowPauseLatched && WindowActive && !WindowMinimized) + { + // The first active sample intentionally keeps the pause latched. + // Poll once more instead of sleeping until unrelated input arrives. + continue; + } else { if ( RwCameraBeginUpdate(Scene.camera) )