Skip to content

Commit 70bd830

Browse files
committed
Release v4.9: raw mouse HWND registration fix, Wine-only raw input hint
1 parent 2f242f6 commit 70bd830

9 files changed

Lines changed: 119 additions & 44 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+
## v4.9
4+
5+
- Raw mouse: register raw input only with HWNDs owned by the game process (`GetWindowThreadProcessId` vs `GetCurrentProcessId`). Resolve the window via `GetGUIThreadInfo`, then `GetActiveWindow` / `GetForegroundWindow` only when they are local, plus `EnumThreadWindows` fallback—fixes `RegisterRawInputDevices` failing with error 87 (`ERROR_INVALID_PARAMETER`) when foreground belonged to another app or at startup. Cvar enable uses the same `raw_mouse_ensure_registered` path as the message pump; clearer log lines for invalid HWND and for error 87.
6+
- Raw mouse: the “Wine/X11 raw input” hint is shown only when actually running under Wine/Proton (`is_running_under_wine()`), not on native Windows failures.
7+
- Core: `is_running_under_wine()` implementation moved to `src/utils/util.cpp` with declaration in `hdr/util.h` (shared helper for updater and raw mouse).
8+
39
## v4.8
410

511
- Updater: refactored zip selection to strict priority order (`preferred_release_zip_names`, `pick_best_zip_url`) instead of scoring; native Windows prefers `release_windows.zip` then `release_windows_xp.zip` when the main zip is missing (fixes Windows 7 receiving wine_linux build). Wine detection hardened (validate `wine_get_version` return).

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.8
1+
4.9

hdr/util.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,7 @@ size_t strlen_custom(const char *str);
136136

137137
const char* get_nth_entry(const char* str, int n);
138138

139+
/** True when running under Wine/Proton (ntdll wine_get_version). */
140+
bool is_running_under_wine();
141+
139142
#endif // SOF_BUDDY_UTIL_H

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 "4.8"
10+
#define SOFBUDDY_VERSION "4.9"

src/core/update_command.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,6 @@ bool is_regular_file(const char* path) {
8181
return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0;
8282
}
8383

84-
bool is_running_under_wine() {
85-
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
86-
if (!ntdll) return false;
87-
typedef const char* (*wine_get_version_fn)(void);
88-
auto fn = reinterpret_cast<wine_get_version_fn>(GetProcAddress(ntdll, "wine_get_version"));
89-
if (!fn) return false;
90-
const char* ver = fn();
91-
if (!ver || !ver[0]) return false;
92-
for (const char* p = ver; *p; ++p)
93-
if (std::isdigit(static_cast<unsigned char>(*p))) return true;
94-
return false;
95-
}
96-
9784
bool prefer_linux_wine_release_zip() {
9885
#if defined(SOFBUDDY_XP_BUILD)
9986
return false;

src/features/raw_mouse/raw_cvars.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ void raw_mouse_on_change(cvar_t *cvar)
2222
PrintOut(PRINT_BAD, "raw_mouse: Raw Input API not available; keeping legacy cursor mode\n");
2323
return;
2424
}
25-
HWND hwnd = GetActiveWindow();
26-
if (!hwnd) hwnd = GetForegroundWindow();
27-
if (raw_mouse_register_input(hwnd, true)) {
25+
raw_mouse_ensure_registered(nullptr, true);
26+
if (raw_mouse_registered) {
2827
PrintOut(PRINT_DEV, "raw_mouse: Raw input is now ENABLED\n");
2928
} else {
30-
PrintOut(PRINT_BAD, "raw_mouse: Raw input was requested but registration failed\n");
29+
PrintOut(PRINT_BAD,
30+
"raw_mouse: Raw input was requested but registration failed\n");
3131
}
3232
} else {
3333
raw_mouse_unregister_input(true);

src/features/raw_mouse/raw_shared.cpp

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void raw_mouse_accumulate_delta(LONG dx, LONG dy) {
7070
}
7171

7272
static bool RawMouseHasForeground(HWND hwnd);
73-
void raw_mouse_ensure_registered(HWND hwnd_hint);
73+
void raw_mouse_ensure_registered(HWND hwnd_hint, bool log_register_attempts);
7474

7575
void raw_mouse_process_raw_mouse() {
7676
#if !SOFBUDDY_RAWINPUT_API_AVAILABLE
@@ -80,7 +80,7 @@ void raw_mouse_process_raw_mouse() {
8080
if (raw_processed_this_frame) return;
8181
raw_processed_this_frame = true;
8282

83-
if (!raw_mouse_registered) raw_mouse_ensure_registered(nullptr);
83+
if (!raw_mouse_registered) raw_mouse_ensure_registered(nullptr, false);
8484
if (raw_mouse_hwnd_target && !RawMouseHasForeground(raw_mouse_hwnd_target)) return;
8585

8686
UINT size = 0;
@@ -141,20 +141,61 @@ void raw_mouse_process_raw_mouse() {
141141
#endif
142142
}
143143

144-
static HWND RawMouseResolveTargetHwnd(HWND hwnd_hint) {
145-
if (raw_mouse_hwnd_target && IsWindow(raw_mouse_hwnd_target)) {
144+
static bool RawMouseHwndIsOurProcess(HWND hwnd) {
145+
if (!hwnd || !IsWindow(hwnd)) return false;
146+
DWORD pid = 0;
147+
GetWindowThreadProcessId(hwnd, &pid);
148+
return pid == GetCurrentProcessId();
149+
}
150+
151+
static BOOL CALLBACK RawMouseEnumThreadTopLevelProc(HWND hwnd, LPARAM lp) {
152+
auto* out = reinterpret_cast<HWND*>(lp);
153+
if (!IsWindowVisible(hwnd)) return TRUE;
154+
if (GetParent(hwnd) != nullptr) return TRUE;
155+
*out = hwnd;
156+
return FALSE;
157+
}
158+
159+
static HWND RawMouseFirstVisibleTopLevelOnCurrentThread() {
160+
HWND found = nullptr;
161+
EnumThreadWindows(GetCurrentThreadId(), RawMouseEnumThreadTopLevelProc,
162+
reinterpret_cast<LPARAM>(&found));
163+
return found;
164+
}
165+
166+
static HWND RawMouseResolveLocalWindow(HWND hwnd_hint) {
167+
if (raw_mouse_hwnd_target && IsWindow(raw_mouse_hwnd_target) &&
168+
RawMouseHwndIsOurProcess(raw_mouse_hwnd_target)) {
146169
return raw_mouse_hwnd_target;
147170
}
148-
149-
if (hwnd_hint && IsWindow(hwnd_hint)) {
171+
if (hwnd_hint && IsWindow(hwnd_hint) && RawMouseHwndIsOurProcess(hwnd_hint)) {
150172
return hwnd_hint;
151173
}
152174

153-
HWND hwnd = GetActiveWindow();
154-
if (!hwnd) {
155-
hwnd = GetForegroundWindow();
175+
GUITHREADINFO gti = {};
176+
gti.cbSize = sizeof(GUITHREADINFO);
177+
if (GetGUIThreadInfo(GetCurrentThreadId(), &gti)) {
178+
HWND h = gti.hwndActive;
179+
if (!h) h = gti.hwndFocus;
180+
if (h && IsWindow(h) && RawMouseHwndIsOurProcess(h)) {
181+
return h;
182+
}
156183
}
157-
return hwnd;
184+
185+
HWND aw = GetActiveWindow();
186+
if (aw && RawMouseHwndIsOurProcess(aw)) return aw;
187+
188+
HWND fg = GetForegroundWindow();
189+
if (fg && RawMouseHwndIsOurProcess(fg)) return fg;
190+
191+
HWND eth = RawMouseFirstVisibleTopLevelOnCurrentThread();
192+
if (eth && RawMouseHwndIsOurProcess(eth)) return eth;
193+
194+
return nullptr;
195+
}
196+
197+
static HWND RawMouseResolveTargetHwnd(HWND hwnd_hint) {
198+
return RawMouseResolveLocalWindow(hwnd_hint);
158199
}
159200

160201
static bool RawMouseHasForeground(HWND hwnd) {
@@ -189,20 +230,25 @@ static HWND RawMouseGetRoot(HWND hwnd) {
189230
#endif
190231
}
191232

192-
void raw_mouse_ensure_registered(HWND hwnd_hint) {
233+
void raw_mouse_ensure_registered(HWND hwnd_hint, bool log_register_attempts) {
193234
if (!raw_mouse_is_enabled() || !raw_mouse_api_supported()) return;
194235
if (raw_mouse_hwnd_target && !IsWindow(raw_mouse_hwnd_target)) {
195236
raw_mouse_registered = false;
196237
raw_mouse_hwnd_target = nullptr;
197238
}
198-
HWND hwnd = (hwnd_hint && IsWindow(hwnd_hint)) ? hwnd_hint : nullptr;
199-
if (!hwnd && raw_mouse_hwnd_target) hwnd = raw_mouse_hwnd_target;
200-
if (!hwnd) hwnd = GetActiveWindow();
201-
if (!hwnd) hwnd = GetForegroundWindow();
202-
if (!hwnd) return;
239+
HWND hwnd = RawMouseResolveLocalWindow(
240+
(hwnd_hint && IsWindow(hwnd_hint)) ? hwnd_hint : nullptr);
241+
if (!hwnd) {
242+
if (log_register_attempts) {
243+
PrintOut(PRINT_BAD,
244+
"raw_mouse: No game window for raw input (focus the game window "
245+
"and try again)\n");
246+
}
247+
return;
248+
}
203249
HWND root = RawMouseGetRoot(hwnd);
204250
if (raw_mouse_registered && raw_mouse_hwnd_target && IsWindow(raw_mouse_hwnd_target) && RawMouseGetRoot(raw_mouse_hwnd_target) == root) return;
205-
raw_mouse_register_input(root, false);
251+
raw_mouse_register_input(root, log_register_attempts);
206252
}
207253

208254
void raw_mouse_release_cursor_clip() {
@@ -285,6 +331,17 @@ bool raw_mouse_register_input(HWND hwnd, bool log_result) {
285331
raw_mouse_release_cursor_clip();
286332
return false;
287333
}
334+
if (!RawMouseHwndIsOurProcess(hwnd)) {
335+
if (log_result) {
336+
PrintOut(PRINT_BAD,
337+
"raw_mouse: Window is not owned by this process (cannot "
338+
"register raw input)\n");
339+
}
340+
raw_mouse_registered = false;
341+
raw_mouse_hwnd_target = nullptr;
342+
raw_mouse_release_cursor_clip();
343+
return false;
344+
}
288345

289346
RAWINPUTDEVICE rid;
290347
rid.usUsagePage = 0x01;
@@ -297,20 +354,22 @@ bool raw_mouse_register_input(HWND hwnd, bool log_result) {
297354
DWORD error = GetLastError();
298355
PrintOut(PRINT_BAD,
299356
"raw_mouse: Failed to register raw input (error %d)\n", error);
300-
PrintOut(PRINT_BAD,
301-
"raw_mouse: On X11/Wine, raw input may not be supported\n");
357+
if (error == ERROR_INVALID_PARAMETER) {
358+
PrintOut(PRINT_BAD,
359+
"raw_mouse: Usually an invalid HWND or wrong thread vs window "
360+
"owner; focus the game and retry\n");
361+
} else if (is_running_under_wine()) {
362+
PrintOut(PRINT_BAD,
363+
"raw_mouse: Under Wine/X11, raw input may be limited; try a "
364+
"newer Proton or native Windows if problems persist\n");
365+
}
302366
}
303367
raw_mouse_registered = false;
304368
raw_mouse_hwnd_target = nullptr;
305369
raw_mouse_release_cursor_clip();
306370
return false;
307371
}
308372

309-
if (log_result) {
310-
PrintOut(PRINT_DEV,
311-
"raw_mouse: Successfully registered raw input device\n");
312-
}
313-
314373
raw_mouse_registered = true;
315374
raw_mouse_hwnd_target = hwnd;
316375
raw_mouse_refresh_cursor_clip(hwnd);

src/features/raw_mouse/shared.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ void raw_mouse_reset_deltas();
4747
void raw_mouse_consume_deltas();
4848
void raw_mouse_update_center(int x, int y);
4949
void raw_mouse_accumulate_delta(LONG dx, LONG dy);
50-
void raw_mouse_ensure_registered(HWND hwnd_hint);
50+
void raw_mouse_ensure_registered(HWND hwnd_hint,
51+
bool log_register_attempts = false);
5152
void raw_mouse_refresh_cursor_clip(HWND hwnd_hint);
5253
void raw_mouse_release_cursor_clip();
5354
bool raw_mouse_register_input(HWND hwnd, bool log_result);

src/utils/util.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <stdarg.h>
55
#include <string.h>
66
#include <stdlib.h>
7+
#include <cctype>
78

89
#include "sof_compat.h"
910
#include "util.h"
@@ -426,4 +427,22 @@ void* rvaToAbsSoFPlus(void* rva) {
426427
#else
427428
return nullptr;
428429
#endif
430+
}
431+
432+
bool is_running_under_wine() {
433+
#ifdef _WIN32
434+
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
435+
if (!ntdll) return false;
436+
typedef const char* (*wine_get_version_fn)(void);
437+
auto fn = reinterpret_cast<wine_get_version_fn>(
438+
GetProcAddress(ntdll, "wine_get_version"));
439+
if (!fn) return false;
440+
const char* ver = fn();
441+
if (!ver || !ver[0]) return false;
442+
for (const char* p = ver; *p; ++p)
443+
if (std::isdigit(static_cast<unsigned char>(*p))) return true;
444+
return false;
445+
#else
446+
return false;
447+
#endif
429448
}

0 commit comments

Comments
 (0)