USB: add direct-to-directinput hack for windows for testing

This commit is contained in:
badfontkeming 2024-11-06 08:33:01 -06:00
parent 2d5faa627f
commit 6d9b9d44a1
4 changed files with 100 additions and 7 deletions

View File

@ -195,13 +195,52 @@ namespace usb_pad
if (m_constant_effect_id < 0)
return;
const s16 new_level = static_cast<s16>(std::clamp(level, -32768, 32767));
s16 new_level = static_cast<s16>(std::clamp(level, -32768, 32767));
if (m_constant_effect.constant.level != new_level)
{
#ifdef __WIN32__
if (bypass_sdl_when_updating)
{
new_level = new_level * 10000 / 32767;
// TODO: Other force types probably need the same type of attention, but constant forces are
// the most sensitive to this issue as they're generally going to be updated the most frequently.
// DANGER! Reading this code may give you radiation poisoning.
// It's ugly, but it works. Ideally SDL would be patched to make this unnecessary,
// but if patching SDL proves to be too unwieldy, a cleaned-up version of this
// might be appropriate.
// It's not easy to identify by hand if you're not highly experienced, so I'd recommend
// average joes use USBPcap via Wireshark in order to verify whether your approach works.
// Try this filter:
// usb.src=="host" && usb.data_len!=0 && usbhid.data
// Steal the raw DirectInput references from SDL and update them directly.
// Allows us to set our own flags for SetParameters. This is important because
// SDL sends unnecessary flags with its updates, which causes unnecessary HID reports,
// which may be causing a loss in detail due to wheels unnecessarily reinitializing
// the force.
// Yeah there's raw C casts, I couldn't figure out how to appease the C++ compiler
// when using stl casts before I had to stop working on this
_SDL_Haptic* real = (_SDL_Haptic*)(m_haptic);
auto ref = real->effects[m_constant_effect_id].hweffect->ref;
auto k = (DICONSTANTFORCE*)real->effects[m_constant_effect_id].hweffect->effect.lpvTypeSpecificParams;
k->lMagnitude = new_level;
ref->SetParameters(&real->effects[m_constant_effect_id].hweffect->effect, DIEP_TYPESPECIFICPARAMS);
}
else
{
m_constant_effect.constant.level = new_level;
if (SDL_HapticUpdateEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0)
Console.Warning("SDL_HapticUpdateEffect() for constant failed: %s", SDL_GetError());
}
#else
m_constant_effect.constant.level = new_level;
if (SDL_HapticUpdateEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0)
Console.Warning("SDL_HapticUpdateEffect() for constant failed: %s", SDL_GetError());
#endif
}
// Avoid re-running already-running effects by default. Re-running a running effect
// causes a variety of issues on different wheels, ranging from quality/detail loss,

View File

@ -5,6 +5,51 @@
#include "USB/usb-pad/usb-pad.h"
#include "Input/SDLInputSource.h"
#ifdef __WIN32__
#include <sdl_haptic.h>
#include <dinput.h>
#endif
#ifdef __WIN32__
// Copied some internal structure definitions from SDL's source
// in order to scoop out their inner bits that are excluded from
// public-facing headers.
// It's ugly, but it works for the purposes of this hack/PoC
struct haptic_hweffect
{
DIEFFECT effect;
LPDIRECTINPUTEFFECT ref;
};
struct haptic_effect
{
SDL_HapticEffect effect; // The current event
struct haptic_hweffect* hweffect; // The hardware behind the event
};
struct _SDL_Haptic
{
Uint8 index; /* Stores index it is attached to */
struct haptic_effect* effects; /* Allocated effects */
int neffects; /* Maximum amount of effects */
int nplaying; /* Maximum amount of effects to play at the same time */
unsigned int supported; /* Supported effects */
int naxes; /* Number of axes on the device. */
struct haptic_hwdata* hwdata; /* Driver dependent */
int ref_count; /* Count for multiple opens */
int rumble_id; /* ID of rumble effect for simple rumble API. */
SDL_HapticEffect rumble_effect; /* Rumble effect. */
struct _SDL_Haptic* next; /* pointer to next haptic we have allocated */
};
#endif
namespace usb_pad
{

View File

@ -166,6 +166,9 @@ namespace usb_pad
"Off", nullptr, nullptr, nullptr, nullptr, SteeringCurveExponentOptions},
{SettingInfo::Type::Boolean, "FfbDropoutWorkaround", TRANSLATE_NOOP("USB", "Workaround for Intermittent FFB Loss"),
TRANSLATE_NOOP("USB", "Works around bugs in some wheels' firmware that result in brief interruptions in force. Leave this disabled unless you need it, as it has negative side effects on many wheels."),
"false"},
{SettingInfo::Type::Boolean, "FfbDirectInputHack", TRANSLATE_NOOP("USB", "HACK: Bypass SDL for FFB updates on Windows"),
TRANSLATE_NOOP("USB", "Forgive me, SDL. It's not you, it's me. (It's you). Has no effect on non-Windows platforms."),
"false"}
};
@ -234,6 +237,11 @@ namespace usb_pad
const bool use_ffb_dropout_workaround = USB::GetConfigBool(si, port, devname, "FfbDropoutWorkaround", false);
mFFdev->use_ffb_dropout_workaround = use_ffb_dropout_workaround;
}
if (mFFdev != NULL)
{
const bool bypass_sdl_when_updating = USB::GetConfigBool(si, port, devname, "FfbDirectInputHack", false);
mFFdev->bypass_sdl_when_updating = bypass_sdl_when_updating;
}
}
}

View File

@ -289,6 +289,7 @@ namespace usb_pad
virtual void DisableForce(EffectID force) = 0;
bool use_ffb_dropout_workaround = false;
bool bypass_sdl_when_updating = false;
};
struct PadState