Consolidate duplicated code.

This commit is contained in:
Jordan Woyak
2026-04-17 16:29:35 -05:00
parent 252c68ee8f
commit fb037aac2b
2 changed files with 109 additions and 139 deletions

View File

@@ -273,12 +273,56 @@ SDL_JoystickID Gamepad::GetSDLInstanceID() const
return SDL_GetJoystickID(m_joystick);
}
class SpringEffect final : public Core::SpringEffect
class ScopedHapticEffect
{
public:
explicit SpringEffect(SDL_Haptic* haptic) : m_haptic{haptic} {}
explicit ScopedHapticEffect(SDL_Haptic* haptic) : m_haptic{haptic} {}
~SpringEffect() override { SetForce(0.0, 0.0); }
protected:
~ScopedHapticEffect() { UpdateEffect(nullptr); }
void UpdateEffect(const SDL_HapticEffect* effect)
{
if (effect == nullptr)
{
if (m_effect_id != -1)
{
SDL_DestroyHapticEffect(m_haptic, std::exchange(m_effect_id, -1));
}
return;
}
if (m_effect_id == -1)
{
// Create and start a new effect.
m_effect_id = SDL_CreateHapticEffect(m_haptic, effect);
if (m_effect_id == -1)
{
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_CreateHapticEffect: {}", SDL_GetError());
return;
}
if (!SDL_RunHapticEffect(m_haptic, m_effect_id, 1))
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_RunHapticEffect: {}", SDL_GetError());
}
else
{
// Update an already running effect.
if (!SDL_UpdateHapticEffect(m_haptic, m_effect_id, effect))
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_UpdateHapticEffect: {}", SDL_GetError());
}
}
private:
SDL_Haptic* const m_haptic;
int m_effect_id = -1;
};
class SpringEffect final : ScopedHapticEffect, public Core::SpringEffect
{
public:
using ScopedHapticEffect::ScopedHapticEffect;
void SetForce(double gain, double center_position) override
{
@@ -286,11 +330,7 @@ public:
if (gain == 0.0)
{
if (m_effect_id != -1)
{
SDL_DestroyHapticEffect(m_haptic, std::exchange(m_effect_id, -1));
}
UpdateEffect(nullptr);
return;
}
@@ -319,38 +359,14 @@ public:
condition.center[0] = ControllerEmu::MapFloat<s16>(center_position, 0);
if (m_effect_id == -1)
{
// Create and start a new effect.
m_effect_id = SDL_CreateHapticEffect(m_haptic, &effect);
if (m_effect_id == -1)
{
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_CreateHapticEffect: {}", SDL_GetError());
return;
}
if (!SDL_RunHapticEffect(m_haptic, m_effect_id, 1))
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_RunHapticEffect: {}", SDL_GetError());
}
else
{
// Update an already running effect.
if (!SDL_UpdateHapticEffect(m_haptic, m_effect_id, &effect))
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_UpdateHapticEffect: {}", SDL_GetError());
}
UpdateEffect(&effect);
}
private:
SDL_Haptic* const m_haptic;
int m_effect_id = -1;
};
class FrictionEffect final : public Core::FrictionEffect
class FrictionEffect final : ScopedHapticEffect, public Core::FrictionEffect
{
public:
explicit FrictionEffect(SDL_Haptic* haptic) : m_haptic{haptic} {}
~FrictionEffect() override { SetForce(0.0); }
using ScopedHapticEffect::ScopedHapticEffect;
void SetForce(double gain) override
{
@@ -358,11 +374,7 @@ public:
if (gain == 0.0)
{
if (m_effect_id != -1)
{
SDL_DestroyHapticEffect(m_haptic, std::exchange(m_effect_id, -1));
}
UpdateEffect(nullptr);
return;
}
@@ -389,30 +401,8 @@ public:
condition.right_coeff[0] = coeff;
condition.left_coeff[0] = coeff;
if (m_effect_id == -1)
{
// Create and start a new effect.
m_effect_id = SDL_CreateHapticEffect(m_haptic, &effect);
if (m_effect_id == -1)
{
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_CreateHapticEffect: {}", SDL_GetError());
return;
}
if (!SDL_RunHapticEffect(m_haptic, m_effect_id, 1))
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_RunHapticEffect: {}", SDL_GetError());
}
else
{
// Update an already running effect.
if (!SDL_UpdateHapticEffect(m_haptic, m_effect_id, &effect))
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL_UpdateHapticEffect: {}", SDL_GetError());
}
UpdateEffect(&effect);
}
private:
SDL_Haptic* const m_haptic;
int m_effect_id = -1;
};
std::unique_ptr<Core::SpringEffect> Gamepad::CreateSpringEffect()

View File

@@ -729,18 +729,17 @@ bool evdevDevice::IsValid() const
return true;
}
class SpringEffect final : public Core::SpringEffect
class ScopedForceFeedbackEffect
{
public:
explicit SpringEffect(int ffb_fd) : m_ffb_fd{ffb_fd} {}
explicit ScopedForceFeedbackEffect(int ffb_fd) : m_ffb_fd{ffb_fd} {}
~SpringEffect() override { SetForce(0.0, 0.0); }
protected:
~ScopedForceFeedbackEffect() { UpdateEffect(nullptr); }
void SetForce(double gain, double center_position) override
void UpdateEffect(ff_effect* effect)
{
INFO_LOG_FMT(CONTROLLERINTERFACE, "SpringEffect: {:.2} {:.2}", gain, center_position);
if (gain == 0.0)
if (effect == nullptr)
{
if (m_effect_id != -1)
{
@@ -756,10 +755,50 @@ public:
return;
}
ff_effect effect{
.type = FF_SPRING,
.id = m_effect_id,
};
effect->id = m_effect_id;
if (ioctl(m_ffb_fd, EVIOCSFF, effect) < 0)
{
ERROR_LOG_FMT(COMMON, "EVIOCSFF: {}", Common::LastStrerrorString());
return;
}
if (m_effect_id == -1)
{
m_effect_id = effect->id;
const input_event play{
.type = EV_FF,
.code = u16(m_effect_id),
.value = std::numeric_limits<s32>::max(),
};
if (write(m_ffb_fd, &play, sizeof(play)) < 0)
ERROR_LOG_FMT(COMMON, "EV_FF: {}", Common::LastStrerrorString());
}
}
private:
const int m_ffb_fd;
s16 m_effect_id = -1;
};
class SpringEffect final : ScopedForceFeedbackEffect, public Core::SpringEffect
{
public:
using ScopedForceFeedbackEffect::ScopedForceFeedbackEffect;
void SetForce(double gain, double center_position) override
{
INFO_LOG_FMT(CONTROLLERINTERFACE, "SpringEffect: {:.2} {:.2}", gain, center_position);
if (gain == 0.0)
{
UpdateEffect(nullptr);
return;
}
ff_effect effect{.type = FF_SPRING};
// <linux/input.h>
// "Values above 32767 ms (0x7fff) should not be used and have unspecified results."
@@ -778,38 +817,14 @@ public:
condition[0].center = ControllerEmu::MapFloat<s16>(center_position, 0);
if (ioctl(m_ffb_fd, EVIOCSFF, &effect) < 0)
{
ERROR_LOG_FMT(COMMON, "EVIOCSFF FF_SPRING: {}", Common::LastStrerrorString());
return;
}
if (m_effect_id == -1)
{
m_effect_id = effect.id;
const input_event play{
.type = EV_FF,
.code = u16(m_effect_id),
.value = std::numeric_limits<s32>::max(),
};
if (write(m_ffb_fd, &play, sizeof(play)) < 0)
ERROR_LOG_FMT(COMMON, "EV_FF: {}", Common::LastStrerrorString());
}
UpdateEffect(&effect);
}
private:
const int m_ffb_fd;
s16 m_effect_id = -1;
};
class FrictionEffect final : public Core::FrictionEffect
class FrictionEffect final : ScopedForceFeedbackEffect, public Core::FrictionEffect
{
public:
explicit FrictionEffect(int ffb_fd) : m_ffb_fd{ffb_fd} {}
~FrictionEffect() override { SetForce(0.0); }
using ScopedForceFeedbackEffect::ScopedForceFeedbackEffect;
void SetForce(double gain) override
{
@@ -817,24 +832,11 @@ public:
if (gain == 0.0)
{
if (m_effect_id != -1)
{
if (ioctl(m_ffb_fd, EVIOCRMFF, m_effect_id) < 0)
ERROR_LOG_FMT(COMMON, "EVIOCRMFF: {}", Common::LastStrerrorString());
// TODO: Is it correct to not remove this effect on destruction.
// Is it enough to close the parent fd ?
m_effect_id = -1;
}
UpdateEffect(nullptr);
return;
}
ff_effect effect{
.type = FF_FRICTION,
.id = m_effect_id,
};
ff_effect effect{.type = FF_FRICTION};
// <linux/input.h>
// "Values above 32767 ms (0x7fff) should not be used and have unspecified results."
@@ -851,30 +853,8 @@ public:
condition[0].right_coeff = coeff;
condition[0].left_coeff = coeff;
if (ioctl(m_ffb_fd, EVIOCSFF, &effect) < 0)
{
ERROR_LOG_FMT(COMMON, "EVIOCSFF FF_SPRING: {}", Common::LastStrerrorString());
return;
}
if (m_effect_id == -1)
{
m_effect_id = effect.id;
const input_event play{
.type = EV_FF,
.code = u16(m_effect_id),
.value = std::numeric_limits<s32>::max(),
};
if (write(m_ffb_fd, &play, sizeof(play)) < 0)
ERROR_LOG_FMT(COMMON, "EV_FF: {}", Common::LastStrerrorString());
}
UpdateEffect(&effect);
}
private:
const int m_ffb_fd;
s16 m_effect_id = -1;
};
std::unique_ptr<Core::SpringEffect> evdevDevice::CreateSpringEffect()