Skip to content

Commit

Permalink
Added Xbox Impulse Trigger->PlayStation Adaptive Trigger emulation fo…
Browse files Browse the repository at this point in the history
…r games using Windows.Gaming.Input (99.99% of games that support the feature)
  • Loading branch information
Kaldaien committed Nov 30, 2024
1 parent c998c73 commit 3efff58
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 44 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
24.11.30.3
24.11.30.4
==========
+ Added Xbox Impulse Trigger->PlayStation Adaptive Trigger emulation for games
using Windows.Gaming.Input (99.99% of games that support the feature).

* Make sure Xbox Mode is enabled and restart if needed; off by default.

24.11.30.3
==========
+ Properly remove Adaptive Trigger force feedback when impulse vibration stops

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 11
#define SK_DATE 30
#define SK_REV_N 3
#define SK_REV 3
#define SK_REV_N 4
#define SK_REV 4

#ifndef _A2
#define _A2(a) #a
Expand Down
12 changes: 4 additions & 8 deletions src/input/hid_reports/playstation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2691,11 +2691,9 @@ SK_HID_PlayStationDevice::write_output_report (bool force)
static_cast <uint8_t> (std::clamp (
static_cast <float> (dwLeftTrigger) *
config.input.gamepad.impulse_strength_l, 0.0f, 1.0f) );
pDevice->_vibration.trigger.last_left = dwLeftTrigger;
} else { output->AllowLeftTriggerFFB = true;
const auto trigger_effect = 0;
memcpy (output->LeftTriggerFFB, effects [trigger_effect], sizeof (effects [trigger_effect]));
pDevice->_vibration.trigger.last_left = dwLeftTrigger;
}
if (dwRightTrigger != 0)
{ output->AllowRightTriggerFFB = true;
Expand All @@ -2705,12 +2703,12 @@ SK_HID_PlayStationDevice::write_output_report (bool force)
static_cast <uint8_t> (std::clamp (
static_cast <float> (dwRightTrigger) *
config.input.gamepad.impulse_strength_r, 0.0f, 1.0f) );
pDevice->_vibration.trigger.last_right = dwRightTrigger;
} else { output->AllowRightTriggerFFB = true;
const auto trigger_effect = 0;
memcpy (output->RightTriggerFFB, effects [trigger_effect], sizeof (effects [trigger_effect]));
pDevice->_vibration.trigger.last_right = dwRightTrigger;
}
pDevice->_vibration.trigger.last_right = dwRightTrigger;
pDevice->_vibration.trigger.last_left = dwLeftTrigger;
}


Expand Down Expand Up @@ -2870,11 +2868,9 @@ SK_HID_PlayStationDevice::write_output_report (bool force)
static_cast <uint8_t> (std::clamp (
static_cast <float> (dwLeftTrigger) *
config.input.gamepad.impulse_strength_l, 0.0f, 1.0f) );
pDevice->_vibration.trigger.last_left = dwLeftTrigger;
} else { output->AllowLeftTriggerFFB = true;
const auto trigger_effect = 0;
memcpy (output->LeftTriggerFFB, effects [trigger_effect], sizeof (effects [trigger_effect]));
pDevice->_vibration.trigger.last_left = dwLeftTrigger;
}
if (dwRightTrigger != 0)
{ output->AllowRightTriggerFFB = true;
Expand All @@ -2884,12 +2880,12 @@ SK_HID_PlayStationDevice::write_output_report (bool force)
static_cast <uint8_t> (std::clamp (
static_cast <float> (dwRightTrigger) *
config.input.gamepad.impulse_strength_r, 0.0f, 1.0f) );
pDevice->_vibration.trigger.last_right = dwRightTrigger;
} else { output->AllowRightTriggerFFB = true;
const auto trigger_effect = 0;
memcpy (output->RightTriggerFFB, effects [trigger_effect], sizeof (effects [trigger_effect]));
pDevice->_vibration.trigger.last_right = dwRightTrigger;
}
pDevice->_vibration.trigger.last_right = dwRightTrigger;
pDevice->_vibration.trigger.last_left = dwLeftTrigger;
}
output->AllowMuteLight = true;

Expand Down
144 changes: 111 additions & 33 deletions src/input/windows.gaming.input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,18 @@ using WGI_GamepadStatistics_get_Gamepads_pfn = HRESULT (STDMETHODCALLTYPE *)(ABI
IVectorView <ABI::Windows::Gaming::Input::Gamepad*> **value);
using WGI_Gamepad_GetCurrentReading_pfn = HRESULT (STDMETHODCALLTYPE *)(ABI::Windows::Gaming::Input::IGamepad *This,
ABI::Windows::Gaming::Input::GamepadReading *value);
using WGI_Gamepad_put_Vibration_pfn = HRESULT (STDMETHODCALLTYPE *)(ABI::Windows::Gaming::Input::IGamepad *This,
ABI::Windows::Gaming::Input::GamepadVibration value);

WGI_GamepadStatistics_get_Gamepads_pfn
WGI_GamepadStatistics_get_Gamepads_Original = nullptr;

WGI_Gamepad_GetCurrentReading_pfn
WGI_Gamepad_GetCurrentReading_Original = nullptr;

WGI_Gamepad_put_Vibration_pfn
WGI_Gamepad_put_Vibration_Original = nullptr;

using WGI_VectorView_Gamepads_GetAt_pfn = HRESULT (STDMETHODCALLTYPE *)(void *This, _In_ unsigned index, _Out_ void **item);
using WGI_VectorView_Gamepads_get_Size_pfn = HRESULT (STDMETHODCALLTYPE *)(void *This, _Out_ unsigned *size);
using WGI_VectorView_Gamepads_IndexOf_pfn = HRESULT (STDMETHODCALLTYPE *)(void *This, _In_opt_ void *value, _Out_ unsigned *index, _Out_ boolean *found);
Expand Down Expand Up @@ -395,44 +400,56 @@ SK_HID_WGI_Gamepad : ABI::Windows::Gaming::Input::IGamepad
{
SK_LOG_FIRST_CALL

vibes = value;

//SK_HID_PlayStationDevice *pNewestInputDevice = nullptr;
bool bRedirected = false;

bool bConnected = false;
SK_HID_PlayStationDevice *pNewestInputDevice = nullptr;

for ( auto& ps_controller : SK_HID_PlayStationControllers )
{
if (ps_controller.bConnected)
{
ps_controller.setVibration (
(std::min (255ui16, static_cast <USHORT> (vibes.LeftMotor * 255.0 + vibes.LeftTrigger * 255.0))),
(std::min (255ui16, static_cast <USHORT> (vibes.RightMotor * 255.0 + vibes.RightTrigger * 255.0))), 255ui16
);

if ((ps_controller.bBluetooth && config.input.gamepad.bt_input_only))
if (pNewestInputDevice == nullptr ||
pNewestInputDevice->xinput.last_active < ps_controller.xinput.last_active)
{
pNewestInputDevice = &ps_controller;
}
}
}

else if ((! (ps_controller.bBluetooth && ps_controller.bSimpleMode)) || (vibes.LeftMotor > 0.0 || vibes.RightMotor > 0.0))
if (pNewestInputDevice != nullptr)
{
if (! SK_ImGui_WantGamepadCapture ())
{
pNewestInputDevice->setVibration (
std::min (65535ui16, static_cast <USHORT> (value.LeftMotor * 65536.0)),
std::min (65535ui16, static_cast <USHORT> (value.RightMotor * 65536.0)),
std::min (65535ui16, static_cast <USHORT> (value.LeftTrigger * 65536.0)),
std::min (65535ui16, static_cast <USHORT> (value.RightTrigger * 65536.0)),
65535ui16
);

// Force an update
//if (pNewestInputDevice->write_output_report (true))
{
if (ps_controller.bBluetooth && (vibes.LeftMotor <= 0.0 && vibes.RightMotor <= 0.0))
{
if (ps_controller.write_output_report ()) // Let the device decide whether to process this or not
bConnected = true;
}
else
{
// Force an update
if (ps_controller.write_output_report (true))
bConnected = true;
}
vibes = value;
bRedirected = true;
}
}

// Swallow vibration while capturing gamepad input
else
{
bRedirected = true;
}
}

if (! bConnected)
SK_XInput_PulseController (0, (float)vibes.LeftMotor, (float)vibes.RightMotor);
// Forward the input to XInput because there was no PlayStation device
if (! bRedirected)
{
vibes = value;
SK_XInput_PulseController (0, static_cast <float>(vibes.LeftMotor),
static_cast <float>(vibes.RightMotor));
}

return S_OK;
}
Expand Down Expand Up @@ -599,6 +616,69 @@ WGI_GamepadStatistics_get_Gamepads_Override ( ABI::Windows::Gaming::Input::IGame

bool SK_WGI_EmulatedPlayStation = false;

HRESULT
STDMETHODCALLTYPE
WGI_Gamepad_put_Vibration_Override (ABI::Windows::Gaming::Input::IGamepad *This,
ABI::Windows::Gaming::Input::GamepadVibration value)
{
SK_LOG_FIRST_CALL

std::ignore = This;

bool bRedirected = false;

SK_HID_PlayStationDevice *pNewestInputDevice = nullptr;

for ( auto& ps_controller : SK_HID_PlayStationControllers )
{
if (ps_controller.bConnected)
{
if (pNewestInputDevice == nullptr ||
pNewestInputDevice->xinput.last_active < ps_controller.xinput.last_active)
{
pNewestInputDevice = &ps_controller;
}
}
}

if (pNewestInputDevice != nullptr)
{
if (! SK_ImGui_WantGamepadCapture ())
{
pNewestInputDevice->setVibration (
std::min (65535ui16, static_cast <USHORT> (value.LeftMotor * 65536.0)),
std::min (65535ui16, static_cast <USHORT> (value.RightMotor * 65536.0)),
std::min (65535ui16, static_cast <USHORT> (value.LeftTrigger * 65536.0)),
std::min (65535ui16, static_cast <USHORT> (value.RightTrigger * 65536.0)),
65535ui16
);

// Force an update
//if (pNewestInputDevice->write_output_report (true))
{
//vibes = value;
bRedirected = true;
}
}

// Swallow vibration while capturing gamepad input
else
{
bRedirected = true;
}
}

// Forward the input to XInput because there was no PlayStation device
if (! bRedirected)
{
//vibes = value;
SK_XInput_PulseController (0, static_cast <float>(value.LeftMotor),
static_cast <float>(value.RightMotor));
}

return S_OK;
}

HRESULT
STDMETHODCALLTYPE
WGI_Gamepad_GetCurrentReading_Override (ABI::Windows::Gaming::Input::IGamepad *This,
Expand Down Expand Up @@ -742,7 +822,7 @@ WGI_Gamepad_GetCurrentReading_Override (ABI::Windows::Gaming::Input::IGamepad

memcpy ( &xi_state, &hid_to_xi, sizeof (XINPUT_STATE) );

value->Timestamp = SK_QueryPerf ().QuadPart;
value->Timestamp = pNewestInputDevice->xinput.last_active;
value->Buttons = GamepadButtons::GamepadButtons_None;

if ((xi_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP))
Expand Down Expand Up @@ -949,7 +1029,7 @@ RoGetActivationFactory_Detour ( _In_ HSTRING activatableClassId,
// 9 remove_GamepadRemoved
// 10 get_Gamepads

if (config.input.gamepad.xinput.emulate && SK_GetCurrentGameID () != SK_GAME_ID::ForzaHorizon5)
if (config.input.gamepad.xinput.emulate)
{
SK_RunOnce ({
WGI_VIRTUAL_HOOK ( &pGamepadStatsFactory, 10,
Expand Down Expand Up @@ -993,19 +1073,17 @@ RoGetActivationFactory_Detour ( _In_ HSTRING activatableClassId,
WGI_Gamepad_GetCurrentReading_Original,
WGI_Gamepad_GetCurrentReading_pfn );

/*
WGI_VIRTUAL_HOOK ( &pGamepad, 6,
"ABI::Windows::Gaming::Input::IGamepad::get_Vibration",
WGI_Gamepad_get_Vibration_Override,
WGI_Gamepad_get_Vibration_Original,
WGI_Gamepad_get_Vibration_pfn );
//WGI_VIRTUAL_HOOK ( &pGamepad, 6,
// "ABI::Windows::Gaming::Input::IGamepad::get_Vibration",
// WGI_Gamepad_get_Vibration_Override,
// WGI_Gamepad_get_Vibration_Original,
// WGI_Gamepad_get_Vibration_pfn );

WGI_VIRTUAL_HOOK ( &pGamepad, 7,
"ABI::Windows::Gaming::Input::IGamepad::put_Vibration",
WGI_Gamepad_put_Vibration_Override,
WGI_Gamepad_put_Vibration_Original,
WGI_Gamepad_put_Vibration_pfn );
*/

SK_ApplyQueuedHooks ();
});
Expand Down

0 comments on commit 3efff58

Please sign in to comment.