diff --git a/LunaDll/Globals.cpp b/LunaDll/Globals.cpp index d3a5bf21..2852ff5c 100644 --- a/LunaDll/Globals.cpp +++ b/LunaDll/Globals.cpp @@ -13,6 +13,7 @@ HINSTANCE gHInstance; HWND gMainWindowHwnd = NULL; bool gMainWindowFocused = false; +bool gMainWindowInBackground = false; // Global settings bool gLunaEnabled; @@ -34,6 +35,7 @@ bool gDisablePlayerFilterBounceFix = false; // Other gameplay settings bool gLavaIsWeak = false; +bool gRunWhenUnfocused = false; // Flag for returning from gameover screen bool gDidGameOver = false; @@ -134,3 +136,6 @@ void printBoxA(const char *fmt, ...) std::string gEditorPlacedItem = "nil"; std::mutex g_editorIPCMutex; + +int gUnfocusTimer = 2; +int gFocusTimer = 2; diff --git a/LunaDll/Globals.h b/LunaDll/Globals.h index 7e037899..30ddb12d 100644 --- a/LunaDll/Globals.h +++ b/LunaDll/Globals.h @@ -90,6 +90,7 @@ extern HINSTANCE gHInstance; /// Global main window state extern HWND gMainWindowHwnd; extern bool gMainWindowFocused; +extern bool gMainWindowInBackground; /// Global settings extern bool gLunaEnabled; @@ -107,6 +108,7 @@ extern bool gDisablePlayerFilterBounceFix; // Other gameplay settings extern bool gLavaIsWeak; +extern bool gRunWhenUnfocused; // Set to true when returning from gameover screen, read by lua to handle gameover-related stuff extern bool gDidGameOver; @@ -212,3 +214,6 @@ return; extern std::string gEditorPlacedItem; extern std::mutex g_editorIPCMutex; + +extern int gUnfocusTimer; +extern int gFocusTimer; diff --git a/LunaDll/Input/Input.cpp b/LunaDll/Input/Input.cpp index 77a8d692..a03c21c1 100644 --- a/LunaDll/Input/Input.cpp +++ b/LunaDll/Input/Input.cpp @@ -80,6 +80,12 @@ void Input::ResetAll() { void Input::UpdateKeyRecords(PlayerMOB* pPlayer) { if(pPlayer == 0) return; + + if(!gMainWindowInBackground) + { + // the window is in the background, so return it + return; + } //wchar_t* dbg = L"Update keys debug"; diff --git a/LunaDll/Input/LunaGameController.cpp b/LunaDll/Input/LunaGameController.cpp index b7072808..9a4e1e84 100644 --- a/LunaDll/Input/LunaGameController.cpp +++ b/LunaDll/Input/LunaGameController.cpp @@ -91,9 +91,12 @@ void LunaGameControllerManager::pollInputs() processSDLEvent(event); } - #if !defined(BUILDING_SMBXLAUNCHER) +#if !defined(BUILDING_SMBXLAUNCHER) + if(!gMainWindowInBackground) + { handleInputs(); - #endif // !defined(BUILDING_SMBXLAUNCHER) + } +#endif // !defined(BUILDING_SMBXLAUNCHER) } // Function to process an SDL event that is incoming diff --git a/LunaDll/Input/MouseHandler.cpp b/LunaDll/Input/MouseHandler.cpp index bda6e60d..10303cd0 100644 --- a/LunaDll/Input/MouseHandler.cpp +++ b/LunaDll/Input/MouseHandler.cpp @@ -8,7 +8,7 @@ MouseHandler gMouseHandler; void MouseHandler::OnMouseMove(int x, int y, uint8_t buttonState) { - if (mInClientArea && (mClientX == x) && (mClientY == y)) + if (mInClientArea && (mClientX == x) && (mClientY == y) && !gMainWindowInBackground) { // No change. return; @@ -43,7 +43,7 @@ void MouseHandler::OnMouseLeave() void MouseHandler::OnMouseButtonEvent(ButtonEnum button, ButtonEvtEnum state) { - if (gLunaLua.isValid()) { + if (gLunaLua.isValid() && !gMainWindowInBackground) { std::shared_ptr event = std::make_shared("onMouseButtonEvent", false); event->setDirectEventName("onMouseButtonEvent"); event->setLoopable(false); @@ -53,7 +53,7 @@ void MouseHandler::OnMouseButtonEvent(ButtonEnum button, ButtonEvtEnum state) void MouseHandler::OnMouseWheelEvent(WheelEnum wheel, int delta) { - if (gLunaLua.isValid()) { + if (gLunaLua.isValid() && !gMainWindowInBackground) { std::shared_ptr event = std::make_shared("onMouseWheelEvent", false); event->setDirectEventName("onMouseWheelEvent"); event->setLoopable(false); diff --git a/LunaDll/LuaMain/LuaProxyFFI.cpp b/LunaDll/LuaMain/LuaProxyFFI.cpp index 3243be9a..3911c8ad 100644 --- a/LunaDll/LuaMain/LuaProxyFFI.cpp +++ b/LunaDll/LuaMain/LuaProxyFFI.cpp @@ -899,3 +899,14 @@ extern "C" { return gLavaIsWeak; } } + +extern "C" { + FFI_EXPORT(void) LunaLuaRunWhenUnfocused(bool value) + { + gRunWhenUnfocused = value; + } + FFI_EXPORT(bool) LunaLuaIsRunningWhenUnfocused(bool value) + { + return gRunWhenUnfocused; + } +} diff --git a/LunaDll/Misc/RuntimeHook.h b/LunaDll/Misc/RuntimeHook.h index aa2c59a7..17caef0d 100644 --- a/LunaDll/Misc/RuntimeHook.h +++ b/LunaDll/Misc/RuntimeHook.h @@ -340,7 +340,14 @@ void __stdcall runtimeHookNPCTerminalVelocityRaw(void); void __stdcall runtimeHookNPCHarmlessGrabRaw(void); void __stdcall runtimeHookNPCHarmlessThrownRaw(void); + +void __stdcall runtimeHookLegacyTitleScreenMouseUp(void); +void __stdcall runtimeHookLegacyTitleScreenMouseDown(void); +void __stdcall runtimeHookLegacyTitleScreenMouseMove(void); + +void __stdcall runtimeHookDoInput(void); void __stdcall runtimeHookCheckInputRaw(void); + void __stdcall runtimeHookSetHDCRaw(void); void __stdcall runtimeHookInitGameHDC(void); diff --git a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp index 52d7ac49..e2138098 100644 --- a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp +++ b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp @@ -604,11 +604,11 @@ static void ProcessRawInput(HWND hwnd, HRAWINPUT hRawInput, bool haveFocus) } // Send lua onRawKeyPress/Release events - if (!repeated) { + if (!repeated && !gMainWindowInBackground) { SendLuaRawKeyEvent(vkey, keyDown); } // If window is focused, and key is down, run keypress handling - if (haveFocus) { + if (haveFocus && !gMainWindowInBackground) { if (keyDown) { ProcessRawKeyPress(vkey, scanCode, repeated); } @@ -865,25 +865,28 @@ LRESULT CALLBACK HandleWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPara // Our main window gained focus? Keep track of that. gMainWindowFocused = true; + gMainWindowInBackground = false; break; case WM_KILLFOCUS: // Unregister VK_SNAPSHOT hotkey handling when out of focus UnregisterHotKey(hwnd, VK_SNAPSHOT); // Our main window lost focus? Keep track of that. - if (!gStartupSettings.runWhenUnfocused) + if (!gRunWhenUnfocused) { gMainWindowFocused = false; } + gMainWindowInBackground = true; break; case WM_DESTROY: // Our main window was destroyed? Clear hwnd and mark as unfocused UnregisterHotKey(hwnd, VK_SNAPSHOT); gMainWindowHwnd = NULL; - if (!gStartupSettings.runWhenUnfocused) + if (!gRunWhenUnfocused) { gMainWindowFocused = false; } + gMainWindowInBackground = true; break; case WM_HOTKEY: if ((wParam == VK_SNAPSHOT) && g_GLEngine.IsEnabled()) @@ -922,10 +925,13 @@ LRESULT CALLBACK HandleWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPara // Process the raw input bool mainWindowFocus = haveFocus && gMainWindowFocused; - ProcessRawInput(hwnd, reinterpret_cast(lParam), mainWindowFocus); + if(!gMainWindowInBackground) + { + ProcessRawInput(hwnd, reinterpret_cast(lParam), mainWindowFocus); + } // If we have focus, return via DefWindowProcW - if (haveFocus) + if (haveFocus && !gMainWindowInBackground) { return DefWindowProcW(hwnd, uMsg, wParam, lParam); } @@ -1202,6 +1208,7 @@ void ParseArgs(const std::vector& args) if (vecStrFind(args, L"--runWhenUnfocused")) { gStartupSettings.runWhenUnfocused = true; + gRunWhenUnfocused = true; gMainWindowFocused = true; } @@ -1715,6 +1722,9 @@ void TrySkipPatch() PATCH(0xA10136).JMP(runtimeHookNPCTerminalVelocityRaw).NOP_PAD_TO_SIZE<58>().Apply(); + PATCH(0x8BDE80).JMP(runtimeHookLegacyTitleScreenMouseDown).NOP_PAD_TO_SIZE<6>().Apply(); + + PATCH(0xA74910).JMP(runtimeHookDoInput).NOP_PAD_TO_SIZE<6>().Apply(); PATCH(0xA75079).JMP(runtimeHookCheckInputRaw).NOP_PAD_TO_SIZE<7>().Apply(); // Hooks for per-npc noblockcollision diff --git a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp index fb1c4a14..77f8c0fc 100644 --- a/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp +++ b/LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp @@ -2016,10 +2016,90 @@ _declspec(naked) void __stdcall runtimeHookNPCTerminalVelocityRaw() } } +__declspec(naked) void __stdcall legacyMouseUp_OrigFunc() +{ + __asm { + push ebp + mov ebp, esp + sub esp,0x0C + push 0x8BE086 + ret + } +} + +void __stdcall runtimeHookLegacyTitleScreenMouseUp() +{ + // Make sure that the mouse from the legacy title screen doesn't do anything when the window is in the background. This is used for running the game when unfocused. + if(!gMainWindowInBackground) + { + legacyMouseUp_OrigFunc(); + } +} + +__declspec(naked) void __stdcall legacyMouseDown_OrigFunc() +{ + __asm { + push ebp + mov ebp, esp + sub esp,0x0C + push 0x8BDE86 + ret + } +} + +void __stdcall runtimeHookLegacyTitleScreenMouseDown() +{ + // Make sure that the mouse from the legacy title screen doesn't do anything when the window is in the background. This is used for running the game when unfocused. + if(!gMainWindowInBackground) + { + legacyMouseDown_OrigFunc(); + } +} + +__declspec(naked) void __stdcall legacyMouseMove_OrigFunc() +{ + __asm { + push ebp + mov ebp, esp + sub esp,0x0C + push 0x8BDF16 + ret + } +} + +void __stdcall runtimeHookLegacyTitleScreenMouseMove() +{ + // Make sure that the mouse from the legacy title screen doesn't do anything when the window is in the background. This is used for running the game when unfocused. + if(!gMainWindowInBackground) + { + legacyMouseMove_OrigFunc(); + } +} + +__declspec(naked) void __stdcall startInput_OrigFunc() +{ + __asm { + push ebp + mov ebp, esp + sub esp, 0x8 + push 0xA74916 + ret + } +} + +void __stdcall runtimeHookDoInput() +{ + // Make sure that inputs don't do anything when the window is in the background. This is used for running the game when unfocused. + if(!gMainWindowInBackground) + { + startInput_OrigFunc(); + } +} + static void __stdcall runtimeHookCheckInput(int playerNum, int playerIdx, KeyMap* keymap) { // Test that player index is in range, and that it matches the true player number (ignore clones) - if ((playerIdx >= 0 && playerIdx <= 1) && ((playerIdx + 1) == playerNum)) + if (((playerIdx >= 0 && playerIdx <= 1) && ((playerIdx + 1) == playerNum))) { gRawKeymap[playerIdx+2] = gRawKeymap[playerIdx]; // Update prev values gRawKeymap[playerIdx] = *keymap; // Set new values @@ -2834,17 +2914,51 @@ void __stdcall runtimeHookCheckWindowFocus() { if (!gMainWindowFocused && !LunaLoadScreenIsActive()) { - // During this block of code, pause music if it was playing - PGE_MusPlayer::DeferralLock musicPauseLock(true); + if(gUnfocusTimer > 0) + { + gUnfocusTimer--; + } + if(gUnfocusTimer == 1) + { + gFocusTimer = 2; + if (gLunaLua.isValid()) { + std::shared_ptr unfocusedEvent = std::make_shared("onUnfocusWindow", false); + unfocusedEvent->setDirectEventName("onUnfocusWindow"); + unfocusedEvent->setLoopable(false); + gLunaLua.callEvent(unfocusedEvent); + } + } + else if(gUnfocusTimer <= 0) + { + // During this block of code, pause music if it was playing + PGE_MusPlayer::DeferralLock musicPauseLock(true); - // Wait for focus - TestModeSendNotification("suspendWhileUnfocusedNotification"); - while (!gMainWindowFocused && !LunaLoadScreenIsActive()) + // Wait for focus + TestModeSendNotification("suspendWhileUnfocusedNotification"); + while (!gMainWindowFocused && !LunaLoadScreenIsActive()) + { + Sleep(100); + LunaDllWaitFrame(false); + } + TestModeSendNotification("resumeAfterUnfocusedNotification"); + } + } + else if (gMainWindowFocused && !LunaLoadScreenIsActive()) + { + if(gFocusTimer > 0) { - Sleep(100); - LunaDllWaitFrame(false); + gFocusTimer--; + } + if(gFocusTimer == 1) + { + gUnfocusTimer = 2; + if (gLunaLua.isValid()) { + std::shared_ptr focusedEvent = std::make_shared("onFocusWindow", false); + focusedEvent->setDirectEventName("onFocusWindow"); + focusedEvent->setLoopable(false); + gLunaLua.callEvent(focusedEvent); + } } - TestModeSendNotification("resumeAfterUnfocusedNotification"); } }