Hello @Lars-4325 ,
Thank you for the detailed context and for sharing the troubleshooting steps you've already taken.
Regarding whether this should be considered a bug, while I can't definitively call it one since there isn't deep official documentation on its under-the-hood implementation, this is a known behavior that many developers have encountered. It seems that the OS-level service responsible for the automatic focus management operates asynchronously from the application's UI thread. When WM_ACTIVATEAPP is triggered, the background system might still be in the process of zeroing out the controller state. Because of this slight timing difference, if you call XInputSetState immediately inside the window message, your command likely gets overridden by the OS completing its automated "clear" sequence. Additionally, it appears the system simply doesn't cache the previous vibration values to restore them for you.
To handle this without constantly polling or spamming XInputSetState, I suggest trying a deferred execution (or delay) pattern. Instead of applying the rumble state immediately upon regaining focus, you can set a flag and wait for a short duration (e.g., 50 to 100 milliseconds, or a few frames) in your main loop. This gives the OS enough time to finish its background transition before you re-apply your cached vibration state.
I suggest incorporating a logic pattern similar to this:
// Globals or class members to cache the state
XINPUT_VIBRATION current_vibration = {0};
bool app_has_focus = true;
bool need_vibration_restore = false;
DWORD focus_regain_time = 0;
// Inside your WindowProc:
case WM_ACTIVATEAPP:
app_has_focus = (wParam == TRUE);
if (app_has_focus) {
need_vibration_restore = true;
focus_regain_time = GetTickCount(); // Record the time focus was regained
} else {
// Optional: explicitly zero-out vibration when losing focus to be safe
XINPUT_VIBRATION zero_vib = {0};
XInputSetState(0, &zero_vib);
}
break;
// Inside your main game loop:
void UpdateGamepad() {
if (!app_has_focus) {
// Skip polling to prevent "bleeding" inputs while in the background
return;
}
// Restore vibration after a short, safe delay (e.g., 100ms)
if (need_vibration_restore) {
if (GetTickCount() - focus_regain_time > 100) {
XInputSetState(0, ¤t_vibration);
need_vibration_restore = false;
}
}
// Normal game logic to update rumble based on gameplay goes here...
}
As a side note, if your project is heavily focused on Windows 10 and newer, you might want to look into the modern Windows.Gaming.Input API down the line. It handles background/foreground focus transitions and modern controller features much more gracefully than legacy XInput.
I hope this helps point you in the right direction. Let me know if the delayed approach works for your main loop. If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation.
Thank you.