Added haptics settings

This commit is contained in:
Marcus Minhorst 2025-08-23 10:44:43 -04:00 committed by Rachael Alexanderson
commit a7a1d8447d
12 changed files with 363 additions and 61 deletions

View file

@ -62,6 +62,13 @@ struct {
bool dirty = false; // do we need to do something next tick ?
bool enabled = true; // do we need to do anything ever ?
bool active = true; // is the game currently not paused ?
struct {
double base = 1.0; // [0,1] -> number <1 turns down rumble strength
double high_frequency = 1.0; // [0,inf)
double low_frequency = 1.0; // [0,inf)
double left_trigger = 1.0; // [0,inf)
double right_trigger = 1.0; // [0,inf)
} strength;
struct Haptics current = {0,0,0,0,0}; // current state of the controller
TMap<FName, struct Haptics> channels; // active rumbles (that will be mixed)
} Haptics;
@ -79,6 +86,48 @@ const FName HapticIntense = "INTENSE",
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// for fine-control, if user wants/needs
// added because trigger haptics are much stronger on xbone controller than expected
CUSTOM_CVARD(Float, haptics_strength_lf, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "low frequency motor fine-control")
{
if (self < 0) self = 0;
Haptics.strength.low_frequency = self * Haptics.strength.base;
};
CUSTOM_CVARD(Float, haptics_strength_hf, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "high frequency motor fine-control")
{
if (self < 0) self = 0;
Haptics.strength.high_frequency = self * Haptics.strength.base;
};
CUSTOM_CVARD(Float, haptics_strength_lt, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "left trigger motor fine-control")
{
if (self < 0) self = 0;
Haptics.strength.left_trigger = self * Haptics.strength.base;
};
CUSTOM_CVARD(Float, haptics_strength_rt, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "right trigger motor fine-control")
{
if (self < 0) self = 0;
Haptics.strength.right_trigger = self * Haptics.strength.base;
};
CUSTOM_CVARD(Int, haptics_strength, 10, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "Translate linear haptics to audio taper")
{
double l1 = self / 10.0;
double l2 = l1 * l1;
double l3 = l2 * l1;
double m3 = 2; // cubed portion
double m2 = -2; // squared portion
double m1 = 1 - m2 - m3; // linear portion
Haptics.enabled = self > 0;
Haptics.strength.base = l1*m1 + l2*m2 + l3*m3;
Haptics.strength.high_frequency = haptics_strength_hf * Haptics.strength.base;
Haptics.strength.low_frequency = haptics_strength_lf * Haptics.strength.base;
Haptics.strength.left_trigger = haptics_strength_lt * Haptics.strength.base;
Haptics.strength.right_trigger = haptics_strength_rt * Haptics.strength.base;
if (!Haptics.enabled) I_Rumble(0, 0, 0, 0);
}
CVARD(Bool, haptics_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "print diagnostics for haptic feedback");
// CODE --------------------------------------------------------------------
@ -349,10 +398,10 @@ void Joy_Rumble(const FName source, const struct Haptics data, double attenuatio
// this will overwrite stuff from same source mapping (weapons/pistol not W_BULLET)
Haptics.channels.Insert(source, {
Haptics.tic + data.ticks + 1,
data.high_frequency * strength,
data.low_frequency * strength,
data.left_trigger * strength,
data.right_trigger * strength,
data.high_frequency * Haptics.strength.high_frequency * strength,
data.low_frequency * Haptics.strength.low_frequency * strength,
data.left_trigger * Haptics.strength.left_trigger * strength,
data.right_trigger * Haptics.strength.right_trigger * strength,
});
Haptics.dirty = true;

View file

@ -4,6 +4,7 @@
** Cross-platform joystick/gamepad management
**
**---------------------------------------------------------------------------
**
** Copyright 2005-2016 Randy Heit
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -30,6 +31,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -172,6 +174,12 @@ bool M_LoadJoystickConfig(IJoystickConfig *joy)
joy->SetSensitivity((float)atof(value));
}
value = GameConfig->GetValueForKey("Haptics");
if (value)
{
joy->SetHapticsStrength((float)atof(value));
}
numaxes = joy->GetNumAxes();
for (int i = 0; i < numaxes; ++i)
{
@ -262,12 +270,19 @@ void M_SaveJoystickConfig(IJoystickConfig *joy)
{
GameConfig->SetValueForKey("EnabledInBackground", "1");
}
if (!joy->IsSensitivityDefault())
{
mysnprintf(value, countof(value), "%g", joy->GetSensitivity());
GameConfig->SetValueForKey("Sensitivity", value);
}
if (!joy->IsHapticsStrengthDefault())
{
mysnprintf(value, countof(value), "%g", joy->GetHapticsStrength());
GameConfig->SetValueForKey("Haptics", value);
}
numaxes = joy->GetNumAxes();
for (int i = 0; i < numaxes; ++i)
{

View file

@ -48,6 +48,10 @@ struct IJoystickConfig
virtual float GetSensitivity() = 0;
virtual void SetSensitivity(float scale) = 0;
virtual bool HasHaptics() = 0;
virtual float GetHapticsStrength() = 0;
virtual void SetHapticsStrength(float strength) = 0;
virtual int GetNumAxes() = 0;
virtual float GetAxisDeadZone(int axis) = 0;
virtual const char *GetAxisName(int axis) = 0;
@ -71,6 +75,7 @@ struct IJoystickConfig
// Used by the saver to not save properties that are at their defaults.
virtual bool IsSensitivityDefault() = 0;
virtual bool IsHapticsStrengthDefault() = 0;
virtual bool IsAxisDeadZoneDefault(int axis) = 0;
virtual bool IsAxisScaleDefault(int axis) = 0;
virtual bool IsAxisDigitalThresholdDefault(int axis) = 0;

View file

@ -4,6 +4,7 @@
** The joystick configuration menus
**
**---------------------------------------------------------------------------
**
** Copyright 2010 Christoph Oelckers
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -30,6 +31,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -54,6 +56,26 @@ DEFINE_ACTION_FUNCTION(IJoystickConfig, SetSensitivity)
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, HasHaptics)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
ACTION_RETURN_BOOL(self->HasHaptics());
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetHapticsStrength)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
ACTION_RETURN_FLOAT(self->GetHapticsStrength());
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetHapticsStrength)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_FLOAT(strength);
self->SetHapticsStrength((float)strength);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisScale)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);

View file

@ -2,6 +2,7 @@
** i_joystick.cpp
**
**---------------------------------------------------------------------------
**
** Copyright 2012-2015 Alexey Lysiuk
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -28,6 +29,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -45,11 +47,9 @@
#include "printf.h"
#include "keydef.h"
EXTERN_CVAR(Bool, joy_axespolling)
EXTERN_CVAR(Bool, use_joystick)
namespace
{
@ -77,10 +77,8 @@ FString ToFString(const CFStringRef string)
return FString(buffer);
}
// ---------------------------------------------------------------------------
class IOKitJoystick : public IJoystickConfig
{
public:
@ -91,6 +89,10 @@ public:
virtual float GetSensitivity();
virtual void SetSensitivity(float scale);
virtual bool HasHaptics();
virtual float GetHapticsStrength();
virtual void SetHapticsStrength(float strength);
virtual int GetNumAxes();
virtual float GetAxisDeadZone(int axis);
virtual const char* GetAxisName(int axis);
@ -106,6 +108,7 @@ public:
void SetAxisResponseCurvePoint(int axis, int point, float value);
virtual bool IsSensitivityDefault();
virtual bool IsHapticsStrengthDefault();
virtual bool IsAxisDeadZoneDefault(int axis);
virtual bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
@ -206,7 +209,6 @@ private:
void RemoveFromQueue(IOHIDElementCookie cookie);
};
IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device)
{
IOCFPlugInInterface** plugInInterface = NULL;
@ -285,7 +287,6 @@ IOHIDQueueInterface** CreateDeviceQueue(IOHIDDeviceInterface** const interface)
return queue;
}
IOKitJoystick::IOKitJoystick(const io_object_t device)
: m_interface(CreateDeviceInterface(device))
, m_queue(CreateDeviceQueue(m_interface))
@ -344,13 +345,11 @@ IOKitJoystick::~IOKitJoystick()
}
}
FString IOKitJoystick::GetName()
{
return m_name;
}
float IOKitJoystick::GetSensitivity()
{
return m_sensitivity;
@ -361,6 +360,20 @@ void IOKitJoystick::SetSensitivity(float scale)
m_sensitivity = scale;
}
bool IOKitJoystick::HasHaptics()
{
return false;
}
float IOKitJoystick::GetHapticsStrength()
{
return JOYHAPSTRENGTH_DEFAULT;
}
void IOKitJoystick::SetHapticsStrength(float strength)
{
// nope
}
int IOKitJoystick::GetNumAxes()
{
@ -448,6 +461,11 @@ bool IOKitJoystick::IsSensitivityDefault()
return JOYSENSITIVITY_DEFAULT == m_sensitivity;
}
bool IOKitJoystick::IsHapticsStrengthDefault()
{
return true;
}
bool IOKitJoystick::IsAxisDeadZoneDefault(int axis)
{
return IS_AXIS_VALID
@ -548,13 +566,11 @@ void IOKitJoystick::SetDefaultConfig()
}
}
FString IOKitJoystick::GetIdentifier()
{
return m_identifier;
}
void IOKitJoystick::AddAxes(float axes[NUM_AXIS_CODES]) const
{
for (size_t i = 0, count = m_axes.Size(); i < count; ++i)
@ -582,7 +598,6 @@ void IOKitJoystick::AddAxes(float axes[NUM_AXIS_CODES]) const
}
}
void IOKitJoystick::UseAxesPolling(const bool axesPolling)
{
m_useAxesPolling = axesPolling;
@ -602,7 +617,6 @@ void IOKitJoystick::UseAxesPolling(const bool axesPolling)
}
}
void IOKitJoystick::Update()
{
if (NULL == m_queue)
@ -630,7 +644,6 @@ void IOKitJoystick::Update()
if(m_enabled) ProcessAxes();
}
void IOKitJoystick::ProcessAxes()
{
if (NULL == m_interface || !m_useAxesPolling)
@ -717,7 +730,6 @@ void IOKitJoystick::ProcessAxes()
}
}
bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event)
{
if (m_useAxesPolling)
@ -809,7 +821,6 @@ bool IOKitJoystick::ProcessPOV(const IOHIDEventStruct& event)
return false;
}
void IOKitJoystick::GatherDeviceInfo(const io_object_t device, const CFDictionaryRef properties)
{
assert(NULL != properties);
@ -914,7 +925,6 @@ void IOKitJoystick::GatherDeviceInfo(const io_object_t device, const CFDictionar
}
}
long GetElementValue(const CFDictionaryRef element, const CFStringRef key)
{
const CFNumberRef number =
@ -991,7 +1001,6 @@ void IOKitJoystick::GatherCollectionElements(const CFDictionaryRef properties)
CFArrayApplyFunction(topElement, range, GatherElementsHandler, this);
}
IOHIDElementCookie GetElementCookie(const CFDictionaryRef element)
{
// Use C-style cast to avoid 32/64-bit IOHIDElementCookie type issue
@ -1039,7 +1048,6 @@ void IOKitJoystick::AddPOV(CFDictionaryRef element)
AddToQueue(pov.cookie);
}
void IOKitJoystick::AddToQueue(const IOHIDElementCookie cookie)
{
if (NULL == m_queue)
@ -1066,16 +1074,13 @@ void IOKitJoystick::RemoveFromQueue(const IOHIDElementCookie cookie)
}
}
io_object_t* IOKitJoystick::GetNotificationPtr()
{
return &m_notification;
}
// ---------------------------------------------------------------------------
class IOKitJoystickManager
{
public:
@ -1109,10 +1114,8 @@ private:
natural_t messageType, void* messageArgument);
};
IOKitJoystickManager* s_joystickManager;
IOKitJoystickManager::IOKitJoystickManager()
{
memset(m_notifications, 0, sizeof m_notifications);
@ -1160,7 +1163,6 @@ IOKitJoystickManager::~IOKitJoystickManager()
}
}
void IOKitJoystickManager::GetJoysticks(TArray<IJoystickConfig*>& joysticks) const
{
const size_t joystickCount = m_joysticks.Size();
@ -1183,7 +1185,6 @@ void IOKitJoystickManager::AddAxes(float axes[NUM_AXIS_CODES]) const
}
}
void IOKitJoystickManager::Update()
{
for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i)
@ -1192,7 +1193,6 @@ void IOKitJoystickManager::Update()
}
}
void IOKitJoystickManager::UseAxesPolling(const bool axesPolling)
{
for (size_t i = 0, count = m_joysticks.Size(); i < count; ++i)
@ -1201,14 +1201,12 @@ void IOKitJoystickManager::UseAxesPolling(const bool axesPolling)
}
}
void PostDeviceChangeEvent()
{
event_t event = { EV_DeviceChange };
D_PostEvent(&event);
}
void IOKitJoystickManager::Rescan(const int usagePage, const int usage, const size_t notificationPortIndex)
{
CFMutableDictionaryRef deviceMatching = IOServiceMatching(kIOHIDDeviceKey);
@ -1270,7 +1268,6 @@ void IOKitJoystickManager::AddDevices(const IONotificationPortRef notificationPo
}
}
void IOKitJoystickManager::OnDeviceAttached(void* const refcon, const io_iterator_t iterator)
{
assert(NULL != refcon);
@ -1309,10 +1306,8 @@ void IOKitJoystickManager::OnDeviceRemoved(void* const refcon, io_service_t, con
} // unnamed namespace
// ---------------------------------------------------------------------------
void I_ShutdownInput()
{
delete s_joystickManager;
@ -1358,10 +1353,8 @@ IJoystickConfig* I_UpdateDeviceList()
return NULL;
}
// ---------------------------------------------------------------------------
void I_ProcessJoysticks()
{
if (NULL != s_joystickManager)
@ -1370,10 +1363,8 @@ void I_ProcessJoysticks()
}
}
// ---------------------------------------------------------------------------
CUSTOM_CVAR(Bool, joy_axespolling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (NULL != s_joystickManager)
@ -1388,3 +1379,4 @@ void I_Rumble(double high_freq, double low_freq, double left_trig, double right_
// stub
}

View file

@ -4,6 +4,7 @@
** Handles sdl joysticks and gamepads
**
**---------------------------------------------------------------------------
**
** Copyright 2005-2016 Randy Heit
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -30,6 +31,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -138,6 +140,25 @@ public:
Multiplier = scale;
}
bool HasHaptics()
{
return Haptics != 0;
}
float GetHapticsStrength()
{
return HasHaptics()
? HapticsStrength
: 0;
}
void SetHapticsStrength(float strength)
{
if (!HasHaptics()) return;
SettingsChanged = true;
HapticsStrength = clamp(strength, 0.f, 2.f);
}
int GetNumAxes()
{
return NumAxes + NumHats*2;
@ -207,6 +228,10 @@ public:
{
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
bool IsHapticsStrengthDefault()
{
return HapticsStrength == JOYHAPSTRENGTH_DEFAULT;
}
bool IsAxisDeadZoneDefault(int axis)
{
if(axis >= DefaultAxesCount)

View file

@ -4,6 +4,7 @@
** Handles direct input joysticks
**
**---------------------------------------------------------------------------
**
** Copyright 2005-2016 Randy Heit
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -30,12 +31,14 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include "m_joy.h"
#define WIN32_LEAN_AND_MEAN
#define DIRECTINPUT_VERSION 0x800
#include <windows.h>
@ -166,6 +169,10 @@ public:
float GetSensitivity();
virtual void SetSensitivity(float scale);
bool HasHaptics();
float GetHapticsStrength();
void SetHapticsStrength(float strength);
int GetNumAxes();
float GetAxisDeadZone(int axis);
const char *GetAxisName(int axis);
@ -181,6 +188,7 @@ public:
void SetAxisResponseCurvePoint(int axis, int point, float value);
bool IsSensitivityDefault();
bool IsHapticsStrengthDefault();
bool IsAxisDeadZoneDefault(int axis);
bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
@ -913,6 +921,50 @@ bool FDInputJoystick::IsSensitivityDefault()
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
//===========================================================================
//
// FDInputJoystick :: HasHaptics
//
//===========================================================================
bool FDInputJoystick::HasHaptics()
{
return false;
}
//===========================================================================
//
// FDInputJoystick :: GetHapticsStrength
//
//===========================================================================
float FDInputJoystick::GetHapticsStrength()
{
return JOYHAPSTRENGTH_DEFAULT;
}
//===========================================================================
//
// FDInputJoystick :: SetHapticsStrength
//
//===========================================================================
void FDInputJoystick::SetHapticsStrength(float strength)
{
// nope
}
//===========================================================================
//
// FDInputJoystick :: IsHapticsStrengthDefault
//
//===========================================================================
bool FDInputJoystick::IsHapticsStrengthDefault()
{
return true;
}
//===========================================================================
//
// FDInputJoystick :: GetNumAxes

View file

@ -2,6 +2,7 @@
**
**
**---------------------------------------------------------------------------
**
** Copyright 2005-2016 Randy Heit
** All rights reserved.
**
@ -27,6 +28,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -98,6 +100,10 @@ public:
float GetSensitivity();
virtual void SetSensitivity(float scale);
bool HasHaptics();
float GetHapticsStrength();
void SetHapticsStrength(float strength);
int GetNumAxes();
float GetAxisDeadZone(int axis);
const char *GetAxisName(int axis);
@ -113,6 +119,7 @@ public:
void SetAxisResponseCurvePoint(int axis, int point, float value);
bool IsSensitivityDefault();
bool IsHapticsStrengthDefault();
bool IsAxisDeadZoneDefault(int axis);
bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
@ -750,6 +757,50 @@ bool FRawPS2Controller::IsSensitivityDefault()
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
//===========================================================================
//
// FRawPS2Controller :: HasHaptics
//
//===========================================================================
bool FRawPS2Controller::HasHaptics()
{
return false;
}
//===========================================================================
//
// FRawPS2Controller :: GetHapticsStrength
//
//===========================================================================
float FRawPS2Controller::GetHapticsStrength()
{
return JOYHAPSTRENGTH_DEFAULT;
}
//===========================================================================
//
// FRawPS2Controller :: SetHapticsStrength
//
//===========================================================================
void FRawPS2Controller::SetHapticsStrength(float strength)
{
// nope
}
//===========================================================================
//
// FRawPS2Controller :: IsHapticsStrengthDefault
//
//===========================================================================
bool FRawPS2Controller::IsHapticsStrengthDefault()
{
return true;
}
//==========================================================================
//
// FRawPS2Controller :: GetNumAxes

View file

@ -4,6 +4,7 @@
** Handles direct input gamepads
**
**---------------------------------------------------------------------------
**
** Copyright 2005-2016 Randy Heit
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -30,6 +31,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -95,6 +97,10 @@ public:
float GetSensitivity();
virtual void SetSensitivity(float scale);
bool HasHaptics();
float GetHapticsStrength();
void SetHapticsStrength(float strength);
int GetNumAxes();
float GetAxisDeadZone(int axis);
const char *GetAxisName(int axis);
@ -110,6 +116,7 @@ public:
void SetAxisResponseCurvePoint(int axis, int point, float value);
bool IsSensitivityDefault();
bool IsHapticsStrengthDefault();
bool IsAxisDeadZoneDefault(int axis);
bool IsAxisScaleDefault(int axis);
bool IsAxisDigitalThresholdDefault(int axis);
@ -575,6 +582,50 @@ bool FXInputController::IsSensitivityDefault()
return Multiplier == JOYSENSITIVITY_DEFAULT;
}
//==========================================================================
//
// FXInputController :: HasHaptics
//
//==========================================================================
bool FXInputController::HasHaptics()
{
return Haptics;
}
//==========================================================================
//
// FXInputController :: GetHapticsStrength
//
//==========================================================================
float FXInputController::GetHapticsStrength()
{
return HapticStrength;
}
//==========================================================================
//
// FXInputController :: SetHapticsStrength
//
//==========================================================================
void FXInputController::SetHapticsStrength(float strength)
{
if (Haptics) HapticStrength = clamp(strength, 0.f, 2.f);
}
//==========================================================================
//
// FXInputController :: IsHapticsStrengthDefault
//
//==========================================================================
bool FXInputController::IsHapticsStrengthDefault()
{
return true;
}
//==========================================================================
//
// FXInputController :: GetNumAxes

View file

@ -365,6 +365,7 @@ OptionMenu "OptionsMenu" protected
Submenu "$OPTMNU_COMPATIBILITY", "CompatibilityOptions"
Submenu "$OPTMNU_AUTOMAP", "AutomapOptions"
Submenu "$OPTMNU_HUD", "HUDOptions"
Submenu "$OPTMNU_HAPTICS", "HapticsOptions"
Submenu "$OPTMNU_MISCELLANEOUS", "MiscOptions"
Submenu "$OPTMNU_NETWORK", "NetworkOptions"
Submenu "$OPTMNU_SOUND", "SoundOptions"
@ -806,7 +807,6 @@ OptionMenu "MouseOptions" protected
Option "$MOUSEMNU_LOOKSTRAFE", "lookstrafe", "OnOff"
}
//-------------------------------------------------------------------------------------------
//
// Joystick Menu
@ -818,6 +818,7 @@ OptionMenu "JoystickOptionsDefaults" protected
Title "$JOYMNU_OPTIONS"
Option "$JOYMNU_ENABLE", "use_joystick", "YesNo"
Option "$JOYMNU_NOMENU", "m_blockcontrollers", "YesNo"
ScaleSlider "$JOYMNU_HAPTICS", "haptics_strength", 0, 10, 1, "$OPTVAL_OFF", 0, "m_simpleoptions_view", 0, 1
IfOption(Windows)
{
Option "$JOYMNU_DINPUT", "joy_dinput", "YesNo"
@ -873,7 +874,6 @@ OptionMenu "JoystickConfigMenu" protected
// Will be filled in by joystick code.
}
//-------------------------------------------------------------------------------------------
//
// Video Menu
@ -1020,7 +1020,6 @@ OptionMenu "VideoOptions" protected
Option "$DSPLYMNU_NOMONSTERINTERPOLATION", "nomonsterinterpolation", "NoYes"
}
//-------------------------------------------------------------------------------------------
//
// HUD menu
@ -1241,6 +1240,21 @@ OptionMenu "AltHUDOptions" protected
option "$ALTHUDMNU_STATSCOLOR", "hudcolor_stats", "TextColors"
}
//-------------------------------------------------------------------------------------------
//
// Haptics menu
//
//-------------------------------------------------------------------------------------------
OptionMenu "HapticsOptions" protected
{
Title "$HAPMNU_TITLE"
ScaleSlider "$JOYMNU_HAPTICS", "haptics_strength", 0, 10, 1, "$OPTVAL_OFF"
StaticText ""
StaticText "$HAPMNU_OPTIONS"
}
//-------------------------------------------------------------------------------------------
//
// Misc menu
@ -1579,7 +1593,6 @@ OptionMenu ColorPickerMenu protected
//
//-------------------------------------------------------------------------------------------
OptionValue MessageLevels
{
0.0, "$OPTVAL_ITEMPICKUP"
@ -1694,7 +1707,6 @@ OptionValue JumpCrouchFreeLook
2, "$OPTVAL_ON"
}
OptionMenu GameplayOptions protected
{
Position -35
@ -1795,7 +1807,6 @@ OptionMenu CoopOptions protected
*
*=======================================*/
OptionValue CompatModes
{
0, "$OPTVAL_DEFAULT"
@ -1890,7 +1901,6 @@ OptionMenu "CompatPhysicsMenu" protected
Class "CompatibilityMenu"
}
OptionMenu "CompatRenderMenu" protected
{
Position -35
@ -1930,7 +1940,6 @@ OptionValue SampleRates
48000, "$OPTVAL_48000HZ"
}
OptionValue BufferSizes
{
0, "$OPTVAL_DEFAULT"
@ -1943,7 +1952,6 @@ OptionValue BufferSizes
4096, "$OPTVAL_4096SAMPLES"
}
OptionValue BufferCounts
{
0, "$OPTVAL_DEFAULT"
@ -1960,7 +1968,6 @@ OptionValue BufferCounts
12, "12"
}
OptionString ALDevices
{
// filled in by the sound code
@ -1983,7 +1990,6 @@ OptionString SpeakerModes
"7.1", "$OPTSTR_7POINT1"
}
OptionString Resamplers
{
"NoInterp", "$OPTSTR_NOINTERPOLATION"
@ -1992,7 +1998,6 @@ OptionString Resamplers
"Spline", "$OPTSTR_SPLINE"
}
OptionMenu OpenALSoundItems protected
{
Title "$OPENALMNU_TITLE"
@ -2001,7 +2006,6 @@ OptionMenu OpenALSoundItems protected
Option "$OPENALMNU_RESAMPLER", "snd_alresampler", "ALResamplers"
}
OptionValue MidiDevices
{
// filled in by the sound code
@ -2051,7 +2055,6 @@ OptionValue OplCores
3, "$OPTVAL_NUKEDOPL3"
}
OptionMenu AdvSoundOptions protected
{
Title "$ADVSNDMNU_TITLE"
@ -2109,14 +2112,12 @@ OptionMenu OPNMIDICustomBanksMenu protected
Title "$ADVSNDMNU_OPNBANKFILE"
}
/*=======================================
*
* Module Replayer Options Menu
*
*=======================================*/
OptionValue ModQuality
{
0.0, "$OPTVAL_ALIASING"
@ -2129,7 +2130,6 @@ OptionValue ModQuality
7.0, "$OPTVAL_SINC"
}
OptionValue ModVolumeRamps
{
0.0, "$OPTVAL_NONE"
@ -2143,7 +2143,6 @@ OptionValue ModReplayers
1.0, "$OPTVAL_DUMB"
}
OptionMenu ModReplayerOptions protected
{
Title "$MODMNU_TITLE"
@ -2397,7 +2396,6 @@ OptionValue MaxFps
200, "$OPTVAL_200FPS"
}
OptionMenu VideoModeMenu protected
{
Title "$VIDMNU_TITLE"
@ -2638,7 +2636,6 @@ OptionValue "BillboardModes"
1, "$OPTVAL_XYAXIS"
}
OptionValue "Particles"
{
0, "$OPTVAL_SQUARE"
@ -2731,7 +2728,6 @@ OptionValue ShadowMapFilter
3, "$OPTVAL_PCF_HIGH"
}
OptionMenu "GLTextureGLOptions" protected
{
Title "$GLTEXMNU_TITLE"

View file

@ -3,6 +3,7 @@
** The joystick configuration menus
**
**---------------------------------------------------------------------------
**
** Copyright 2010-2017 Christoph Oelckers
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -29,6 +30,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -67,6 +69,34 @@ class OptionMenuSliderJoySensitivity : OptionMenuSliderBase
//
//=============================================================================
class OptionMenuSliderJoyHapticsStrength : OptionMenuSliderBase
{
JoystickConfig mJoy;
OptionMenuSliderJoyHapticsStrength Init(String label, double min, double max, double step, int showval, JoystickConfig joy)
{
Super.Init(label, min, max, step, showval);
mJoy = joy;
return self;
}
override double GetSliderValue()
{
return mJoy.GetHapticsStrength();
}
override void SetSliderValue(double val)
{
mJoy.SetHapticsStrength(val);
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuSliderJoyScale : OptionMenuSliderBase
{
int mAxis;
@ -492,8 +522,16 @@ class OptionMenuItemJoyConfigMenu : OptionMenuItemSubmenu
it = new("OptionMenuSliderJoySensitivity").Init("$JOYMNU_OVRSENS", 0, 2, 0.1, 3, joy);
opt.mItems.Push(it);
if (joy.HasHaptics())
{
it = new("OptionMenuSliderJoyHapticsStrength").Init("$JOYMNU_HAPTICS", 0, 2, 0.1, 3, joy);
opt.mItems.Push(it);
}
it = new("OptionMenuJoyReset").Init("$JOYMNU_RESETALL", joy);
opt.mItems.Push(it);
it = new("OptionMenuItemStaticText").Init(" ", false);
opt.mItems.Push(it);

View file

@ -3,6 +3,7 @@
** The menu engine core
**
**---------------------------------------------------------------------------
**
** Copyright 2010-2020 Christoph Oelckers
** Copyright 2017-2025 GZDoom Maintainers and Contributors
** All rights reserved.
@ -29,6 +30,7 @@
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**---------------------------------------------------------------------------
**
*/
@ -81,6 +83,10 @@ struct JoystickConfig native version("2.4")
native float GetSensitivity();
native void SetSensitivity(float scale);
native bool HasHaptics();
native float GetHapticsStrength();
native void SetHapticsStrength(float strength);
native float GetAxisScale(int axis);
native void SetAxisScale(int axis, float scale);