Skip to content

Commit

Permalink
Add backup input thread for games that stop processing input on their…
Browse files Browse the repository at this point in the history
… message queue for long periods of time (i.e. during FMV playback or loading screens).
  • Loading branch information
Kaldaien committed Dec 30, 2024
1 parent e876d98 commit 1744286
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 14 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
24.12.29.4
24.12.29.5
==========
+ Add backup input thread for games that stop processing input on their
message queue for long periods of time (i.e. during FMV playback),
so that SK's keyboard macros and basic mouse input (no scrollwheel)
remain responsive.

* Or, in the worst-case, this backup thread handles all Keyboard & Mouse
because it wasn't possible to hook the game's window thread.

24.12.29.4
==========
+ Removed foreground window HWND caching, the WinEventHookProc does
not order the events it sends coherently.
Expand Down
4 changes: 2 additions & 2 deletions include/SpecialK/DLL_VERSION.H
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#define SK_YEAR 24
#define SK_MONTH 12
#define SK_DATE 29
#define SK_REV_N 4
#define SK_REV 4
#define SK_REV_N 5
#define SK_REV 5

#ifndef _A2
#define _A2(a) #a
Expand Down
65 changes: 54 additions & 11 deletions include/imgui/imgui_user.inl
Original file line number Diff line number Diff line change
Expand Up @@ -3240,26 +3240,68 @@ SK_ImGui_UpdateClassCursor (void)
}
}

BOOL bCursorThreadIsAttached = FALSE;
HANDLE hPollCursorEvent;
bool SK_ImGui_IsHWCursorVisible = false;

extern DWORD SK_ImGui_LastKeyboardProcMessageTime;
extern DWORD SK_ImGui_LastMouseProcMessageTime;

DWORD
WINAPI
SK_ImGui_CursorPollingThread (LPVOID)
SK_ImGui_BackupInputThread (LPVOID)
{
SK_Thread_SetCurrentPriority (THREAD_PRIORITY_ABOVE_NORMAL);
SK_Thread_SetCurrentPriority (THREAD_PRIORITY_NORMAL);

const HANDLE hEvents [] =
{ __SK_DLL_TeardownEvent,
hPollCursorEvent };

while (WaitForMultipleObjects (2, hEvents, FALSE, 1) != WAIT_OBJECT_0)
DWORD dwWaitState = WAIT_TIMEOUT;
while (dwWaitState != WAIT_OBJECT_0)
{
//SK_GetCursorPos (&SK_ImGui_LastKnownCursorPos);
SK_ImGui_LastKnownCursor =
SK_GetCursor ();
SK_ImGui_IsHWCursorVisible =
SK_InputUtil_IsHWCursorVisible ();
dwWaitState =
WaitForMultipleObjects (2, hEvents, FALSE, 2);

// Sometimes games stop processing their message loop for long periods
// of time (i.e. FMV playback or load screens), so we need to resort
// to an alternate means of getting keyboard state.
if (SK_ImGui_LastKeyboardProcMessageTime < SK::ControlPanel::current_time - 500UL &&
(dwWaitState == (WAIT_OBJECT_0 + 1)))
{
auto& io =
ImGui::GetIO ();

bool last_keys [256] = {};
memcpy (last_keys, io.KeysDown, 256);

for (UINT i = 7 ; i < 255 ; ++i)
{
bool last_state =
last_keys [i];
io.KeysDown [i] =
((SK_GetAsyncKeyState (i) & 0x8000) != 0x0);

if (last_state != io.KeysDown [i])
{
if (io.KeysDown [i]) SK_Console::getInstance ()->KeyDown ((BYTE)(i & 0xFF), MAXDWORD);
else SK_Console::getInstance ()->KeyUp ((BYTE)(i & 0xFF), MAXDWORD);
}
}
}

// Sometimes games stop processing their message loop for long periods
// of time (i.e. FMV playback or load screens), so we need to resort
// to an alternate means of getting cursor position.
if (SK_ImGui_LastMouseProcMessageTime < SK::ControlPanel::current_time - 250UL)
{
SK_GetCursorPos (&SK_ImGui_LastKnownCursorPos);
}

SK_ImGui_LastKnownCursor =
SK_GetCursor ();
SK_ImGui_IsHWCursorVisible =
SK_InputUtil_IsHWCursorVisible ();
}

SK_Thread_CloseSelf ();
Expand All @@ -3276,12 +3318,13 @@ SK_ImGui_User_NewFrame (void)

SK_ImGui_UpdateClassCursor ();

POINT cursor_pos = SK_ImGui_LastKnownCursorPos;
POINT cursor_pos = SK_ImGui_LastKnownCursorPos;

SK_RunOnce (
hPollCursorEvent =
SK_CreateEvent (nullptr, FALSE, FALSE, nullptr);
SK_Thread_CreateEx (SK_ImGui_CursorPollingThread,
L"[SK] CursorPollingThread", nullptr);
SK_Thread_CreateEx (SK_ImGui_BackupInputThread,
L"[SK] Backup Input Thread", nullptr);
);

SetEvent (hPollCursorEvent);
Expand Down
7 changes: 7 additions & 0 deletions src/control_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7247,13 +7247,18 @@ SK_Platform_GetUserName (char* pszName, int max_len = 512)

extern POINT SK_ImGui_LastKnownCursorPos;

DWORD SK_ImGui_LastMouseProcMessageTime = 0;
DWORD SK_ImGui_LastKeyboardProcMessageTime = 0;

LRESULT
CALLBACK
SK_ImGui_MouseProc (int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0 || GImGui == nullptr) // We saw nothing (!!)
return CallNextHookEx (0, code, wParam, lParam);

SK_ImGui_LastMouseProcMessageTime = SK::ControlPanel::current_time;

auto& io =
ImGui::GetIO ();

Expand Down Expand Up @@ -7467,6 +7472,8 @@ SK_ImGui_KeyboardProc (int code, WPARAM wParam, LPARAM lParam)
if (code < 0 || GImGui == nullptr) // We saw nothing (!!)
return CallNextHookEx (0, code, wParam, lParam);

SK_ImGui_LastKeyboardProcMessageTime = SK::ControlPanel::current_time;

const bool keyboard_capture =
SK_ImGui_WantKeyboardCapture ();

Expand Down

0 comments on commit 1744286

Please sign in to comment.