Skip to content

Commit 5cd7f2c

Browse files
committed
Release v5.3: raw input registration fallback, GUI-thread deferral
1 parent d4cc299 commit 5cd7f2c

7 files changed

Lines changed: 99 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v5.3
4+
5+
- Raw mouse: if `RegisterRawInputDevices` returns `ERROR_INVALID_PARAMETER` (87) for focus-following registration (`hwndTarget=NULL`), retry once with an explicit top-level HWND resolved from the game window and foreground heuristics.
6+
- Raw mouse: call `RegisterRawInputDevices` only on the thread that owns `cl_hwnd`; other threads defer. `DispatchMessageA` retries registration while raw mouse is enabled and not yet registered so deferred work completes on the game message thread.
7+
- Raw mouse: when enable is deferred, the `_sofbuddy_rawmouse` cvar path logs that registration is pending instead of reporting a hard failure.
8+
39
## v5.2
410

511
- Raw mouse: stop binding normal foreground raw input registration to a specific HWND. `RegisterRawInputDevices` now uses focus-following mode (`hwndTarget = NULL`), while window resolution is kept only for cursor clip/focus decisions. This avoids `ERROR_INVALID_PARAMETER` 87 from bad or overlay-polluted HWNDs and lets registration succeed earlier in startup.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
5.2
1+
5.3

hdr/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
Increment version using: ./increment_version.sh
88
*/
99

10-
#define SOFBUDDY_VERSION "5.2"
10+
#define SOFBUDDY_VERSION "5.3"

src/features/raw_mouse/hooks/dispatchmessagea.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ static bool msg_affects_cursor_clip(UINT msg) {
3434
LRESULT dispatchmessagea_override_callback(
3535
const MSG *msg, detour_DispatchMessageA::tDispatchMessageA original) {
3636
if (msg && raw_mouse_is_enabled() && raw_mouse_api_supported()) {
37+
/* Finish registration on the GUI thread after deferred attempts from
38+
* other threads (and before clip-only events). */
39+
if (!raw_mouse_registered) {
40+
raw_mouse_ensure_registered(msg->hwnd, false);
41+
}
3742
if (msg_affects_cursor_clip(msg->message)) {
3843
raw_mouse_ensure_registered(msg->hwnd);
3944
raw_mouse_refresh_cursor_clip(msg->hwnd);

src/features/raw_mouse/raw_cvars.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ void raw_mouse_on_change(cvar_t *cvar)
2828
raw_mouse_ensure_registered(nullptr, true);
2929
if (raw_mouse_registered) {
3030
PrintOut(PRINT_DEV, "raw_mouse: Raw input is now ENABLED\n");
31+
} else if (raw_mouse_reg_deferred_to_gui_thread) {
32+
PrintOut(PRINT_LOG,
33+
"raw_mouse: Raw input enable pending (registration will "
34+
"complete on the game message thread)\n");
3135
} else {
3236
PrintOut(PRINT_BAD,
3337
"raw_mouse: Raw input was requested but registration failed\n");

src/features/raw_mouse/raw_shared.cpp

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ int raw_mouse_delta_y = 0;
1313
POINT window_center = {0, 0};
1414
bool raw_mouse_center_valid = false;
1515
bool raw_mouse_registered = false;
16+
bool raw_mouse_reg_deferred_to_gui_thread = false;
1617
bool raw_mouse_cursor_clipped = false;
1718
HWND raw_mouse_hwnd_target = nullptr;
1819
static RECT raw_mouse_clip_rect = {0, 0, 0, 0};
@@ -426,22 +427,82 @@ static void RawMouseDropRegistration() {
426427
}
427428

428429
#if SOFBUDDY_RAWINPUT_API_AVAILABLE
429-
static bool RawMouseCommitRawInputRegistration(bool log_result) {
430+
enum class RawMouseRegResult { Ok, Failed, Deferred };
431+
432+
/* RegisterRawInputDevices is only reliable from the thread that owns the game's
433+
* message queue. Other threads (e.g. GetCursorPos hook) must defer. */
434+
static bool RawMouseOnGameGuiThread() {
435+
HWND gw = RawMouseGameWindowHwnd();
436+
if (!gw || !IsWindow(gw)) {
437+
return true;
438+
}
439+
DWORD tid = 0;
440+
GetWindowThreadProcessId(gw, &tid);
441+
return GetCurrentThreadId() == tid;
442+
}
443+
444+
static HWND RawMouseFallbackRegistrationHwnd() {
445+
HWND w = RawMouseResolveLocalWindow(nullptr);
446+
if (w) {
447+
return w;
448+
}
449+
HWND fg = GetForegroundWindow();
450+
if (fg && RawMouseHwndIsOurProcess(fg)) {
451+
return RawMouseNormalizeCandidateHwnd(fg);
452+
}
453+
return nullptr;
454+
}
455+
456+
static RawMouseRegResult RawMouseCommitRawInputRegistration(bool log_result) {
457+
if (!RawMouseOnGameGuiThread()) {
458+
if (log_result) {
459+
PrintOut(PRINT_DEV,
460+
"raw_mouse: Deferring raw input registration to the game GUI "
461+
"thread (RegisterRawInputDevices is not reliable off-thread)\n");
462+
}
463+
return RawMouseRegResult::Deferred;
464+
}
465+
430466
RAWINPUTDEVICE rid = {};
431467
rid.usUsagePage = 0x01;
432468
rid.usUsage = 0x02;
433469
rid.dwFlags = 0;
434470
rid.hwndTarget = nullptr;
435471

436472
if (!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))) {
473+
const DWORD err_null = GetLastError();
474+
bool tried_fallback = false;
475+
if (err_null == ERROR_INVALID_PARAMETER) {
476+
HWND fallback = RawMouseFallbackRegistrationHwnd();
477+
if (fallback) {
478+
tried_fallback = true;
479+
rid.hwndTarget = fallback;
480+
if (RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))) {
481+
raw_mouse_registered = true;
482+
if (log_result) {
483+
PrintOut(PRINT_DEV,
484+
"raw_mouse: Raw input registration succeeded using "
485+
"explicit top-level hwnd (focus-following NULL was "
486+
"rejected with error %lu)\n",
487+
static_cast<unsigned long>(err_null));
488+
RawMouseLogWindowDetails("registration hwndTarget", fallback,
489+
PRINT_DEV);
490+
RawMouseLogRegisteredMouseDevice(PRINT_DEV);
491+
}
492+
return RawMouseRegResult::Ok;
493+
}
494+
}
495+
}
496+
const DWORD err_report = tried_fallback ? GetLastError() : err_null;
437497
if (log_result) {
438-
DWORD error = GetLastError();
439498
PrintOut(PRINT_BAD,
440-
"raw_mouse: Failed to register raw input (error %d)\n", error);
441-
if (error == ERROR_INVALID_PARAMETER) {
499+
"raw_mouse: Failed to register raw input (error %lu)\n",
500+
static_cast<unsigned long>(err_report));
501+
if (err_report == ERROR_INVALID_PARAMETER) {
442502
PrintOut(PRINT_BAD,
443-
"raw_mouse: Parameter validation failed even with "
444-
"focus-following registration (hwndTarget=NULL)\n");
503+
"raw_mouse: Parameter validation failed for focus-following "
504+
"(NULL)%s\n",
505+
tried_fallback ? " and explicit-hwnd fallback" : "");
445506
RawMouseLogResolutionState(nullptr, PRINT_BAD);
446507
RawMouseLogRegisteredMouseDevice(PRINT_BAD);
447508
} else if (is_running_under_wine()) {
@@ -451,7 +512,7 @@ static bool RawMouseCommitRawInputRegistration(bool log_result) {
451512
}
452513
}
453514
RawMouseDropRegistration();
454-
return false;
515+
return RawMouseRegResult::Failed;
455516
}
456517

457518
raw_mouse_registered = true;
@@ -461,7 +522,7 @@ static bool RawMouseCommitRawInputRegistration(bool log_result) {
461522
"(focus-following mode, hwndTarget=NULL)\n");
462523
RawMouseLogRegisteredMouseDevice(PRINT_DEV);
463524
}
464-
return true;
525+
return RawMouseRegResult::Ok;
465526
}
466527
#endif
467528

@@ -491,7 +552,17 @@ void raw_mouse_ensure_registered(HWND hwnd_hint, bool log_register_attempts) {
491552
return;
492553
}
493554
#if SOFBUDDY_RAWINPUT_API_AVAILABLE
494-
if (!RawMouseCommitRawInputRegistration(log_register_attempts)) return;
555+
switch (RawMouseCommitRawInputRegistration(log_register_attempts)) {
556+
case RawMouseRegResult::Ok:
557+
raw_mouse_reg_deferred_to_gui_thread = false;
558+
break;
559+
case RawMouseRegResult::Deferred:
560+
raw_mouse_reg_deferred_to_gui_thread = true;
561+
return;
562+
case RawMouseRegResult::Failed:
563+
raw_mouse_reg_deferred_to_gui_thread = false;
564+
return;
565+
}
495566
#endif
496567

497568
if (raw_mouse_hwnd_target) {

src/features/raw_mouse/shared.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ extern int raw_mouse_delta_y;
3434
extern POINT window_center;
3535
extern bool raw_mouse_center_valid;
3636
extern bool raw_mouse_registered;
37+
/** True when RegisterRawInputDevices was skipped because we are not on the game GUI thread. */
38+
extern bool raw_mouse_reg_deferred_to_gui_thread;
3739
extern bool raw_mouse_cursor_clipped;
3840
extern HWND raw_mouse_hwnd_target;
3941

0 commit comments

Comments
 (0)