diff --git a/UnleashedRecomp/cfg/config.h b/UnleashedRecomp/cfg/config.h index 9378be2b..3ab7969b 100644 --- a/UnleashedRecomp/cfg/config.h +++ b/UnleashedRecomp/cfg/config.h @@ -21,6 +21,7 @@ public: CONFIG_DEFINE_LOCALISED("Controls", bool, CameraYInvert, false); CONFIG_DEFINE_LOCALISED("Controls", bool, XButtonHoming, true); CONFIG_DEFINE_LOCALISED("Controls", bool, UnleashCancel, false); + CONFIG_DEFINE_LOCALISED("Controls", bool, BackgroundInput, false); CONFIG_DEFINE_LOCALISED("Audio", float, MusicVolume, 1.0f); CONFIG_DEFINE_LOCALISED("Audio", float, SEVolume, 1.0f); diff --git a/UnleashedRecomp/cfg/config_locale.h b/UnleashedRecomp/cfg/config_locale.h index a9a0e220..8d7f22ea 100644 --- a/UnleashedRecomp/cfg/config_locale.h +++ b/UnleashedRecomp/cfg/config_locale.h @@ -137,6 +137,16 @@ CONFIG_DEFINE_DESCRIPTION_LOCALE(LogoSkip) { ELanguage::English, "Show the logos during the game's boot sequence." } }; +CONFIG_DEFINE_LOCALE(BackgroundInput) +{ + { ELanguage::English, "Allow Background Input" } +}; + +CONFIG_DEFINE_DESCRIPTION_LOCALE(BackgroundInput) +{ + { ELanguage::English, "Set whether to accept controller input whilst the game window is unfocused." } +}; + CONFIG_DEFINE_LOCALE(CameraXInvert) { { ELanguage::English, "Invert Camera X" } diff --git a/UnleashedRecomp/hid/driver/sdl_hid.cpp b/UnleashedRecomp/hid/driver/sdl_hid.cpp index bcd89498..d9465448 100644 --- a/UnleashedRecomp/hid/driver/sdl_hid.cpp +++ b/UnleashedRecomp/hid/driver/sdl_hid.cpp @@ -1,6 +1,10 @@ #include #include +#include #include +#include + +#define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X) #define VIBRATION_TIMEOUT_MS 5000 class Controller @@ -13,17 +17,12 @@ public: XAMINPUT_VIBRATION vibration{ 0, 0 }; Controller() = default; - explicit Controller(int index) : Controller(SDL_GameControllerOpen(index)) - { - - } + explicit Controller(int index) : Controller(SDL_GameControllerOpen(index)) {} Controller(SDL_GameController* controller) : controller(controller) { if (!controller) - { return; - } joystick = SDL_GameControllerGetJoystick(controller); id = SDL_JoystickInstanceID(joystick); @@ -31,20 +30,28 @@ public: void Close() { - if (controller == nullptr) - { + if (!controller) return; - } SDL_GameControllerClose(controller); + controller = nullptr; joystick = nullptr; id = -1; } + bool CanPoll() + { + return controller && (Window::s_isFocused || Config::BackgroundInput); + } + void PollAxis() { + if (!CanPoll()) + return; + auto& pad = state; + pad.sThumbLX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); pad.sThumbLY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); @@ -55,14 +62,13 @@ public: pad.bRightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 7; } - #define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X) void Poll() { - if (controller == nullptr) - { + if (!CanPoll()) return; - } + auto& pad = state; + pad.wButtons = 0; pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_UP, XAMINPUT_GAMEPAD_DPAD_UP); @@ -87,12 +93,11 @@ public: void SetVibration(const XAMINPUT_VIBRATION& vibration) { - if (controller == nullptr) - { + if (!CanPoll()) return; - } this->vibration = vibration; + SDL_GameControllerRumble(controller, vibration.wLeftMotorSpeed * 256, vibration.wRightMotorSpeed * 256, VIBRATION_TIMEOUT_MS); } }; @@ -102,9 +107,7 @@ std::array g_controllers; inline Controller* EnsureController(DWORD dwUserIndex) { if (!g_controllers[dwUserIndex].controller) - { return nullptr; - } return &g_controllers[dwUserIndex]; } @@ -114,9 +117,7 @@ inline size_t FindFreeController() for (size_t i = 0; i < g_controllers.size(); i++) { if (!g_controllers[i].controller) - { return i; - } } return -1; @@ -127,9 +128,7 @@ inline Controller* FindController(int which) for (auto& controller : g_controllers) { if (controller.id == which) - { return &controller; - } } return nullptr; @@ -142,22 +141,21 @@ int HID_OnSDLEvent(void*, SDL_Event* event) if (event->type == SDL_CONTROLLERDEVICEADDED) { const auto freeIndex = FindFreeController(); + if (freeIndex != -1) - { g_controllers[freeIndex] = Controller(event->cdevice.which); - } } if (event->type == SDL_CONTROLLERDEVICEREMOVED) { auto* controller = FindController(event->cdevice.which); + if (controller) - { controller->Close(); - } } else if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP || event->type == SDL_CONTROLLERAXISMOTION) { auto* controller = FindController(event->cdevice.which); + if (controller) { if (event->type == SDL_CONTROLLERAXISMOTION) @@ -181,6 +179,7 @@ void hid::detail::Init() SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1"); SDL_InitSubSystem(SDL_INIT_EVENTS); @@ -193,62 +192,58 @@ void hid::detail::Init() uint32_t hid::detail::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState) { static DWORD packet; + if (!pState) - { return ERROR_BAD_ARGUMENTS; - } memset(pState, 0, sizeof(*pState)); + pState->dwPacketNumber = packet++; SDL_JoystickUpdate(); - auto* controller = EnsureController(dwUserIndex); - if (controller == nullptr) - { + + if (!EnsureController(dwUserIndex)) return ERROR_DEVICE_NOT_CONNECTED; - } pState->Gamepad = g_controllers[dwUserIndex].state; + return ERROR_SUCCESS; } uint32_t hid::detail::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration) { if (!pVibration) - { return ERROR_BAD_ARGUMENTS; - } SDL_JoystickUpdate(); + auto* controller = EnsureController(dwUserIndex); - if (controller == nullptr) - { + + if (!controller) return ERROR_DEVICE_NOT_CONNECTED; - } controller->SetVibration(*pVibration); + return ERROR_SUCCESS; } uint32_t hid::detail::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps) { if (!pCaps) - { return ERROR_BAD_ARGUMENTS; - } SDL_JoystickUpdate(); + auto* controller = EnsureController(dwUserIndex); - if (controller == nullptr) - { + + if (!controller) return ERROR_DEVICE_NOT_CONNECTED; - } memset(pCaps, 0, sizeof(*pCaps)); + pCaps->Type = XAMINPUT_DEVTYPE_GAMEPAD; pCaps->SubType = XAMINPUT_DEVSUBTYPE_GAMEPAD; // TODO: other types? pCaps->Flags = 0; - pCaps->Gamepad = controller->state; pCaps->Vibration = controller->vibration; diff --git a/UnleashedRecomp/kernel/xam.cpp b/UnleashedRecomp/kernel/xam.cpp index bc9842d5..bd704bc6 100644 --- a/UnleashedRecomp/kernel/xam.cpp +++ b/UnleashedRecomp/kernel/xam.cpp @@ -333,9 +333,6 @@ SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_S { //printf("!!! STUB !!! XamInputGetState\n"); - if (!Window::s_isFocused) - return 0; - uint32_t result = hid::GetState(userIndex, state); if (result == ERROR_SUCCESS) @@ -349,6 +346,9 @@ SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_S } else if (userIndex == 0) { + if (!Window::s_isFocused) + return ERROR_SUCCESS; + memset(state, 0, sizeof(*state)); if (GetAsyncKeyState('W') & 0x8000) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y; diff --git a/UnleashedRecomp/ui/options_menu.cpp b/UnleashedRecomp/ui/options_menu.cpp index 5532360e..93c0b9a8 100644 --- a/UnleashedRecomp/ui/options_menu.cpp +++ b/UnleashedRecomp/ui/options_menu.cpp @@ -663,6 +663,7 @@ static void DrawConfigOptions() DrawConfigOption(rowCount++, yOffset, &Config::CameraYInvert); DrawConfigOption(rowCount++, yOffset, &Config::XButtonHoming); DrawConfigOption(rowCount++, yOffset, &Config::UnleashCancel); + DrawConfigOption(rowCount++, yOffset, &Config::BackgroundInput); break; case 2: // AUDIO DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume);