Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Window focus events, WM_KILLFOCUS input fixes, and the addition of "Misc.runWhenUnfocused(bool)" #71

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions LunaDll/Globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ HINSTANCE gHInstance;

HWND gMainWindowHwnd = NULL;
bool gMainWindowFocused = false;
bool gMainWindowInBackground = false;

// Global settings
bool gLunaEnabled;
Expand All @@ -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;
Expand Down Expand Up @@ -134,3 +136,6 @@ void printBoxA(const char *fmt, ...)

std::string gEditorPlacedItem = "nil";
std::mutex g_editorIPCMutex;

int gUnfocusTimer = 2;
int gFocusTimer = 2;
5 changes: 5 additions & 0 deletions LunaDll/Globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -212,3 +214,6 @@ return;

extern std::string gEditorPlacedItem;
extern std::mutex g_editorIPCMutex;

extern int gUnfocusTimer;
extern int gFocusTimer;
6 changes: 6 additions & 0 deletions LunaDll/Input/Input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
7 changes: 5 additions & 2 deletions LunaDll/Input/LunaGameController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions LunaDll/Input/MouseHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> event = std::make_shared<Event>("onMouseButtonEvent", false);
event->setDirectEventName("onMouseButtonEvent");
event->setLoopable(false);
Expand All @@ -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> event = std::make_shared<Event>("onMouseWheelEvent", false);
event->setDirectEventName("onMouseWheelEvent");
event->setLoopable(false);
Expand Down
11 changes: 11 additions & 0 deletions LunaDll/LuaMain/LuaProxyFFI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
7 changes: 7 additions & 0 deletions LunaDll/Misc/RuntimeHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 16 additions & 6 deletions LunaDll/Misc/RuntimeHookComponents/RuntimeHookGeneral.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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<HRAWINPUT>(lParam), mainWindowFocus);
if(!gMainWindowInBackground)
{
ProcessRawInput(hwnd, reinterpret_cast<HRAWINPUT>(lParam), mainWindowFocus);
}

// If we have focus, return via DefWindowProcW
if (haveFocus)
if (haveFocus && !gMainWindowInBackground)
{
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
Expand Down Expand Up @@ -1202,6 +1208,7 @@ void ParseArgs(const std::vector<std::wstring>& args)
if (vecStrFind(args, L"--runWhenUnfocused"))
{
gStartupSettings.runWhenUnfocused = true;
gRunWhenUnfocused = true;
gMainWindowFocused = true;
}

Expand Down Expand Up @@ -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
Expand Down
132 changes: 123 additions & 9 deletions LunaDll/Misc/RuntimeHookComponents/RuntimeHookHooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Event> unfocusedEvent = std::make_shared<Event>("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<Event> focusedEvent = std::make_shared<Event>("onFocusWindow", false);
focusedEvent->setDirectEventName("onFocusWindow");
focusedEvent->setLoopable(false);
gLunaLua.callEvent(focusedEvent);
}
}
TestModeSendNotification("resumeAfterUnfocusedNotification");
}
}

Expand Down