mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-27 16:15:40 +00:00
ab367774d3
In xinput mode, lets the choice to have left and right gamepad motors vibrate together. XInputStereoVibration = 1 (both motors) [default] XInputStereoVibration = 0 (separate motors) In sdl input mode, new control option to set minimum strength above which a Model 3 constant force command will be simulated on an sdl gamepad device. SDLConstForceThreshold = 30 [default] note : the vibration strength can be mod with SDLConstForceMax = [val]
829 lines
29 KiB
C++
829 lines
29 KiB
C++
/**
|
|
** Supermodel
|
|
** A Sega Model 3 Arcade Emulator.
|
|
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
|
**
|
|
** This file is part of Supermodel.
|
|
**
|
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
|
** the terms of the GNU General Public License as published by the Free
|
|
** Software Foundation, either version 3 of the License, or (at your option)
|
|
** any later version.
|
|
**
|
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
** more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License along
|
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
/*
|
|
* SDLInputSystem.cpp
|
|
*
|
|
* Implementation of SDL input system.
|
|
*
|
|
* TODO:
|
|
* -----
|
|
* - Implement multiple keyboard and mouse support.
|
|
*/
|
|
|
|
#include "Supermodel.h"
|
|
#include "SDLInputSystem.h"
|
|
|
|
#include <vector>
|
|
using namespace std;
|
|
|
|
SDLKeyMapStruct CSDLInputSystem::s_keyMap[] =
|
|
{
|
|
// General keys
|
|
{ "BACKSPACE", SDL_SCANCODE_BACKSPACE },
|
|
{ "TAB", SDL_SCANCODE_TAB },
|
|
{ "CLEAR", SDL_SCANCODE_CLEAR },
|
|
{ "RETURN", SDL_SCANCODE_RETURN },
|
|
{ "PAUSE", SDL_SCANCODE_PAUSE },
|
|
{ "ESCAPE", SDL_SCANCODE_ESCAPE },
|
|
{ "SPACE", SDL_SCANCODE_SPACE },
|
|
{ "QUOTE", SDL_SCANCODE_APOSTROPHE },
|
|
{ "LEFTPAREN", SDL_SCANCODE_KP_LEFTPAREN },
|
|
{ "RIGHTPAREN", SDL_SCANCODE_KP_RIGHTPAREN },
|
|
{ "COMMA", SDL_SCANCODE_COMMA },
|
|
{ "MINUS", SDL_SCANCODE_MINUS },
|
|
{ "PERIOD", SDL_SCANCODE_PERIOD },
|
|
{ "SLASH", SDL_SCANCODE_SLASH },
|
|
{ "0", SDL_SCANCODE_0 },
|
|
{ "1", SDL_SCANCODE_1 },
|
|
{ "2", SDL_SCANCODE_2 },
|
|
{ "3", SDL_SCANCODE_3 },
|
|
{ "4", SDL_SCANCODE_4 },
|
|
{ "5", SDL_SCANCODE_5 },
|
|
{ "6", SDL_SCANCODE_6 },
|
|
{ "7", SDL_SCANCODE_7 },
|
|
{ "8", SDL_SCANCODE_8 },
|
|
{ "9", SDL_SCANCODE_9 },
|
|
{ "SEMICOLON", SDL_SCANCODE_SEMICOLON },
|
|
{ "EQUALS", SDL_SCANCODE_EQUALS },
|
|
{ "LEFTBRACKET", SDL_SCANCODE_LEFTBRACKET },
|
|
{ "BACKSLASH", SDL_SCANCODE_BACKSLASH },
|
|
{ "RIGHTBRACKET", SDL_SCANCODE_RIGHTBRACKET },
|
|
{ "BACKQUOTE", SDL_SCANCODE_GRAVE },
|
|
{ "A", SDL_SCANCODE_A },
|
|
{ "B", SDL_SCANCODE_B },
|
|
{ "C", SDL_SCANCODE_C },
|
|
{ "D", SDL_SCANCODE_D },
|
|
{ "E", SDL_SCANCODE_E },
|
|
{ "F", SDL_SCANCODE_F },
|
|
{ "G", SDL_SCANCODE_G },
|
|
{ "H", SDL_SCANCODE_H },
|
|
{ "I", SDL_SCANCODE_I },
|
|
{ "J", SDL_SCANCODE_J },
|
|
{ "K", SDL_SCANCODE_K },
|
|
{ "L", SDL_SCANCODE_L },
|
|
{ "M", SDL_SCANCODE_M },
|
|
{ "N", SDL_SCANCODE_N },
|
|
{ "O", SDL_SCANCODE_O },
|
|
{ "P", SDL_SCANCODE_P },
|
|
{ "Q", SDL_SCANCODE_Q },
|
|
{ "R", SDL_SCANCODE_R },
|
|
{ "S", SDL_SCANCODE_S },
|
|
{ "T", SDL_SCANCODE_T },
|
|
{ "U", SDL_SCANCODE_U },
|
|
{ "V", SDL_SCANCODE_V },
|
|
{ "W", SDL_SCANCODE_W },
|
|
{ "X", SDL_SCANCODE_X },
|
|
{ "Y", SDL_SCANCODE_Y },
|
|
{ "Z", SDL_SCANCODE_Z },
|
|
{ "DEL", SDL_SCANCODE_DELETE },
|
|
|
|
// Keypad
|
|
{ "KEYPAD0", SDL_SCANCODE_KP_0 },
|
|
{ "KEYPAD1", SDL_SCANCODE_KP_1 },
|
|
{ "KEYPAD2", SDL_SCANCODE_KP_2 },
|
|
{ "KEYPAD3", SDL_SCANCODE_KP_3 },
|
|
{ "KEYPAD4", SDL_SCANCODE_KP_4 },
|
|
{ "KEYPAD5", SDL_SCANCODE_KP_5 },
|
|
{ "KEYPAD6", SDL_SCANCODE_KP_6 },
|
|
{ "KEYPAD7", SDL_SCANCODE_KP_7 },
|
|
{ "KEYPAD8", SDL_SCANCODE_KP_8 },
|
|
{ "KEYPAD9", SDL_SCANCODE_KP_9 },
|
|
{ "KEYPADPERIOD", SDL_SCANCODE_KP_PERIOD },
|
|
{ "KEYPADDIVIDE", SDL_SCANCODE_KP_DIVIDE },
|
|
{ "KEYPADMULTIPLY", SDL_SCANCODE_KP_MULTIPLY },
|
|
{ "KEYPADMINUS", SDL_SCANCODE_KP_MINUS },
|
|
{ "KEYPADPLUS", SDL_SCANCODE_KP_PLUS },
|
|
{ "KEYPADENTER", SDL_SCANCODE_KP_ENTER },
|
|
{ "KEYPADEQUALS", SDL_SCANCODE_KP_EQUALS },
|
|
|
|
// Arrows + Home/End Pad
|
|
{ "UP", SDL_SCANCODE_UP },
|
|
{ "DOWN", SDL_SCANCODE_DOWN },
|
|
{ "RIGHT", SDL_SCANCODE_RIGHT },
|
|
{ "LEFT", SDL_SCANCODE_LEFT },
|
|
{ "INSERT", SDL_SCANCODE_INSERT },
|
|
{ "HOME", SDL_SCANCODE_HOME },
|
|
{ "END", SDL_SCANCODE_END },
|
|
{ "PGUP", SDL_SCANCODE_PAGEUP },
|
|
{ "PGDN", SDL_SCANCODE_PAGEDOWN },
|
|
|
|
// Function Key
|
|
{ "F1", SDL_SCANCODE_F1 },
|
|
{ "F2", SDL_SCANCODE_F2 },
|
|
{ "F3", SDL_SCANCODE_F3 },
|
|
{ "F4", SDL_SCANCODE_F4 },
|
|
{ "F5", SDL_SCANCODE_F5 },
|
|
{ "F6", SDL_SCANCODE_F6 },
|
|
{ "F7", SDL_SCANCODE_F7 },
|
|
{ "F8", SDL_SCANCODE_F8 },
|
|
{ "F9", SDL_SCANCODE_F9 },
|
|
{ "F10", SDL_SCANCODE_F10 },
|
|
{ "F11", SDL_SCANCODE_F11 },
|
|
{ "F12", SDL_SCANCODE_F12 },
|
|
{ "F13", SDL_SCANCODE_F13 },
|
|
{ "F14", SDL_SCANCODE_F14 },
|
|
{ "F15", SDL_SCANCODE_F15 },
|
|
|
|
// Modifier Keys
|
|
// Removed Numlock, Capslock and Scrollock as don't seem to be handled well by SDL
|
|
//{ "NUMLOCK", SDLK_NUMLOCK },
|
|
//{ "CAPSLOCK", SDL_SCANCODE_CAPSLOCK },
|
|
//{ "SCROLLLOCK", SDLK_SCROLLOCK },
|
|
{ "RIGHTSHIFT", SDL_SCANCODE_RSHIFT },
|
|
{ "LEFTSHIFT", SDL_SCANCODE_LSHIFT },
|
|
{ "RIGHTCTRL", SDL_SCANCODE_RCTRL },
|
|
{ "LEFTCTRL", SDL_SCANCODE_LCTRL },
|
|
{ "RIGHTALT", SDL_SCANCODE_RALT },
|
|
{ "LEFTALT", SDL_SCANCODE_LALT },
|
|
{ "RIGHTMETA", SDL_SCANCODE_RGUI },
|
|
{ "LEFTMETA", SDL_SCANCODE_LGUI },
|
|
{ "ALTGR", SDL_SCANCODE_MODE },
|
|
|
|
// Other
|
|
{ "HELP", SDL_SCANCODE_HELP },
|
|
{ "SYSREQ", SDL_SCANCODE_SYSREQ },
|
|
{ "MENU", SDL_SCANCODE_MENU },
|
|
{ "POWER", SDL_SCANCODE_POWER },
|
|
{ "UNDO", SDL_SCANCODE_UNDO }
|
|
};
|
|
|
|
CSDLInputSystem::CSDLInputSystem(const Util::Config::Node& config)
|
|
: CInputSystem("SDL"),
|
|
m_keyState(nullptr),
|
|
m_mouseX(0),
|
|
m_mouseY(0),
|
|
m_mouseZ(0),
|
|
m_mouseButtons(0),
|
|
m_config(config)
|
|
{
|
|
//
|
|
}
|
|
|
|
CSDLInputSystem::~CSDLInputSystem()
|
|
{
|
|
CloseJoysticks();
|
|
}
|
|
|
|
void CSDLInputSystem::OpenJoysticks()
|
|
{
|
|
// Open all available joysticks
|
|
int numJoys = SDL_NumJoysticks();
|
|
int numHapticAxes = 0;
|
|
int possibleEffect = 0;
|
|
|
|
for (int joyNum = 0; joyNum < numJoys; joyNum++)
|
|
{
|
|
numHapticAxes = 0;
|
|
SDL_Joystick *joystick = SDL_JoystickOpen(joyNum);
|
|
if (joystick == nullptr)
|
|
{
|
|
ErrorLog("Unable to open joystick device %d with SDL - skipping joystick.\n", joyNum + 1);
|
|
continue;
|
|
}
|
|
|
|
// Gather joystick details (name, num POVs & buttons and which axes are available)
|
|
JoyDetails joyDetails;
|
|
hapticInfo hapticDatas;
|
|
const char *pName = SDL_JoystickName(joystick);
|
|
strncpy(joyDetails.name, pName, MAX_NAME_LENGTH);
|
|
joyDetails.name[MAX_NAME_LENGTH] = '\0';
|
|
joyDetails.numAxes = SDL_JoystickNumAxes(joystick);
|
|
|
|
if (SDL_JoystickIsHaptic(joystick))
|
|
joyDetails.hasFFeedback = true;
|
|
else
|
|
joyDetails.hasFFeedback = false;
|
|
|
|
if (joyDetails.hasFFeedback)
|
|
{
|
|
hapticDatas.SDLhaptic = SDL_HapticOpenFromJoystick(joystick);
|
|
|
|
if (hapticDatas.SDLhaptic == NULL)
|
|
{
|
|
ErrorLog("Unable to obtain haptic interface for joystick %s. Force feedback will be disabled for this joystick.", joyDetails.name);
|
|
joyDetails.hasFFeedback = false;
|
|
numHapticAxes = 0;
|
|
}
|
|
else
|
|
{
|
|
numHapticAxes = SDL_HapticNumAxes(hapticDatas.SDLhaptic);
|
|
|
|
// depending device, SDL_HapticNumAxes return wrong number of ffb axes (bug on device driver on windows or other ?)
|
|
// ie : saitek cyborg evo force joystick returns 3 ffb axes instead of 2 ffb axes, this leads to unable to create effects on this device
|
|
// generally, none of commercial ffb products have more than 2 ffb axes
|
|
// in this case, we need to force the correct number of ffb axes by adding manually a new sdl2 function
|
|
// see https://forums.libsdl.org/viewtopic.php?t=5195
|
|
// enabled this code if you have a saitek cyborg evo force or if you find another device that return wrong number of ffb axis
|
|
// don't forget to edit sdl2 source code in Supermodel project
|
|
//#define HAPTIC_MOD
|
|
#if (defined _WIN32 && defined HAPTIC_MOD)
|
|
if (numHapticAxes > 2)
|
|
{
|
|
DebugLog("Joystick : %s return more than 2 ffb axes, need sdl2 addon SDL_HapticSetAxes() to bypass\n", SDL_HapticName(joyNum));
|
|
if (strcmp(joyDetails.name, "Saitek Cyborg Evo Force") == 0) // remove if any other particular case
|
|
SDL_HapticSetAxes(hapticDatas.SDLhaptic, 2); // /!\ function manually added to SDL2 project
|
|
}
|
|
#endif
|
|
|
|
|
|
// sdl2 bug on linux only ?
|
|
// SDL_HapticNumAxes() : on win it reports good number of haptic axe, on linux 18.04 it reports always 2 haptic axes (bad) whatever the device
|
|
// file : SDL2-2.0.10\src\haptic\linux\SDL_syshaptic.c
|
|
// function : static int SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic, int fd)
|
|
// haptic->naxes = 2; // Hardcoded for now, not sure if it's possible to find out. <- note from the sdl2 devs
|
|
#ifndef _WIN32
|
|
if (!HasBasicForce(hapticDatas.SDLhaptic)) numHapticAxes = 0;
|
|
#endif
|
|
DebugLog("joy num %d haptic num axe %d name : %s\n", joyNum, numHapticAxes, SDL_HapticName(joyNum));
|
|
}
|
|
}
|
|
|
|
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
|
|
{
|
|
joyDetails.hasAxis[axisNum] = joyDetails.numAxes > axisNum;
|
|
if (numHapticAxes > 0 && joyDetails.hasAxis[axisNum]) // unable to know which axes have ffb
|
|
joyDetails.axisHasFF[axisNum] = true;
|
|
else
|
|
joyDetails.axisHasFF[axisNum] = false;
|
|
|
|
char *axisName = joyDetails.axisName[axisNum];
|
|
strcpy(axisName, CInputSystem::GetDefaultAxisName(axisNum));
|
|
}
|
|
joyDetails.numPOVs = SDL_JoystickNumHats(joystick);
|
|
joyDetails.numButtons = SDL_JoystickNumButtons(joystick);
|
|
|
|
if (joyDetails.hasFFeedback && hapticDatas.SDLhaptic != NULL && numHapticAxes > 0) // not a pad but wheel or joystick
|
|
{
|
|
SDL_HapticSetAutocenter(hapticDatas.SDLhaptic, 0);
|
|
SDL_HapticSetGain(hapticDatas.SDLhaptic, 100);
|
|
|
|
possibleEffect = SDL_HapticQuery(hapticDatas.SDLhaptic);
|
|
|
|
// constant effect
|
|
if (possibleEffect & SDL_HAPTIC_CONSTANT)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_CONSTANT;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.constant.direction.dir[0] = 0;
|
|
eff.constant.length = 30;
|
|
eff.constant.delay = 0;
|
|
eff.constant.level = 0;
|
|
hapticDatas.effectConstantForceID = SDL_HapticNewEffect(hapticDatas.SDLhaptic, &eff);;
|
|
if (hapticDatas.effectConstantForceID < 0)
|
|
ErrorLog("Unable to create constant force effect for joystick %s (joy id=%d). Constant force will not be applied.", joyDetails.name, joyNum + 1);
|
|
}
|
|
|
|
// vibration effect
|
|
if (possibleEffect & SDL_HAPTIC_SINE)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_SINE;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.constant.length = 500;
|
|
eff.constant.delay = 0;
|
|
eff.periodic.period = 50;
|
|
eff.periodic.magnitude = 0;
|
|
hapticDatas.effectVibrationID = SDL_HapticNewEffect(hapticDatas.SDLhaptic, &eff);
|
|
if (hapticDatas.effectVibrationID < 0)
|
|
ErrorLog("Unable to create vibration effect for joystick %s (joy id=%d). Vibration will not be applied.", joyDetails.name, joyNum + 1);
|
|
}
|
|
|
|
// spring effect
|
|
if (possibleEffect & SDL_HAPTIC_SPRING)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_SPRING;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.condition.delay = 0;
|
|
eff.condition.length = SDL_HAPTIC_INFINITY;
|
|
eff.condition.left_sat[0] = 0xFFFF;
|
|
eff.condition.right_sat[0] = 0xFFFF;
|
|
eff.condition.left_coeff[0] = 0;
|
|
eff.condition.right_coeff[0] = 0;
|
|
hapticDatas.effectSpringForceID = SDL_HapticNewEffect(hapticDatas.SDLhaptic, &eff);
|
|
if (hapticDatas.effectSpringForceID < 0)
|
|
ErrorLog("Unable to create spring force effect for joystick %s (joy id=%d). Spring force will not be applied.", joyDetails.name, joyNum + 1);
|
|
}
|
|
|
|
// friction effect
|
|
if (possibleEffect & SDL_HAPTIC_FRICTION)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_FRICTION;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.condition.delay = 0;
|
|
eff.condition.length = SDL_HAPTIC_INFINITY;
|
|
eff.condition.left_sat[0] = 0xFFFF;
|
|
eff.condition.right_sat[0] = 0xFFFF;
|
|
eff.condition.left_coeff[0] = 0;
|
|
eff.condition.right_coeff[0] = 0;
|
|
hapticDatas.effectFrictionForceID = SDL_HapticNewEffect(hapticDatas.SDLhaptic, &eff);
|
|
if (hapticDatas.effectFrictionForceID < 0)
|
|
ErrorLog("Unable to create friction force effect for joystick %s (joy id=%d). Friction force will not be applied.", joyDetails.name, joyNum + 1);
|
|
}
|
|
}
|
|
|
|
if (joyDetails.hasFFeedback && hapticDatas.SDLhaptic != NULL && numHapticAxes == 0) // pad with rumble. Note : SDL_HapticRumbleSupported() is not enough to detect rumble pad only because joystick or wheel may have also rumble
|
|
{
|
|
if (SDL_HapticRumbleInit(hapticDatas.SDLhaptic) < 0)
|
|
{
|
|
ErrorLog("Unable to create rumble effect for pad %s (pad id=%d). Rumble will not be applied.SDL_HapticRumbleInit failed : %s", joyDetails.name, joyNum + 1, SDL_GetError());
|
|
joyDetails.axisHasFF[AXIS_X] = false;
|
|
joyDetails.axisHasFF[AXIS_Y] = false;
|
|
}
|
|
else
|
|
{
|
|
joyDetails.axisHasFF[AXIS_X] = true; // Force feedback simulated on X axis sticks (fake)
|
|
joyDetails.axisHasFF[AXIS_Y] = true; // Force feedback simulated on Y axis sticks (fake)
|
|
}
|
|
}
|
|
|
|
m_joysticks.push_back(joystick);
|
|
m_joyDetails.push_back(joyDetails);
|
|
m_SDLHapticDatas.push_back(hapticDatas);
|
|
}
|
|
}
|
|
|
|
void CSDLInputSystem::CloseJoysticks()
|
|
{
|
|
// Close all previously opened joysticks
|
|
for (size_t i = 0; i < m_joysticks.size(); i++)
|
|
{
|
|
JoyDetails joyDetails = m_joyDetails[i];
|
|
if (joyDetails.hasFFeedback)
|
|
{
|
|
if (m_SDLHapticDatas[i].effectConstantForceID >= 0)
|
|
SDL_HapticDestroyEffect(m_SDLHapticDatas[i].SDLhaptic, m_SDLHapticDatas[i].effectConstantForceID);
|
|
if (m_SDLHapticDatas[i].effectVibrationID >= 0)
|
|
SDL_HapticDestroyEffect(m_SDLHapticDatas[i].SDLhaptic, m_SDLHapticDatas[i].effectVibrationID);
|
|
if (m_SDLHapticDatas[i].effectSpringForceID >= 0)
|
|
SDL_HapticDestroyEffect(m_SDLHapticDatas[i].SDLhaptic, m_SDLHapticDatas[i].effectSpringForceID);
|
|
if (m_SDLHapticDatas[i].effectFrictionForceID >= 0)
|
|
SDL_HapticDestroyEffect(m_SDLHapticDatas[i].SDLhaptic, m_SDLHapticDatas[i].effectFrictionForceID);
|
|
|
|
SDL_HapticClose(m_SDLHapticDatas[i].SDLhaptic);
|
|
}
|
|
SDL_Joystick *joystick = m_joysticks[i];
|
|
SDL_JoystickClose(joystick);
|
|
}
|
|
|
|
m_joysticks.clear();
|
|
m_joyDetails.clear();
|
|
m_SDLHapticDatas.clear();
|
|
}
|
|
|
|
bool CSDLInputSystem::InitializeSystem()
|
|
{
|
|
// Make sure joystick subsystem is initialized and joystick events are enabled
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0)
|
|
{
|
|
ErrorLog("Unable to initialize SDL joystick subsystem (%s).\n", SDL_GetError());
|
|
|
|
return false;
|
|
}
|
|
SDL_JoystickEventState(SDL_ENABLE);
|
|
|
|
// Open attached joysticks
|
|
OpenJoysticks();
|
|
return true;
|
|
}
|
|
|
|
int CSDLInputSystem::GetKeyIndex(const char *keyName)
|
|
{
|
|
for (int i = 0; i < NUM_SDL_KEYS; i++)
|
|
{
|
|
if (stricmp(keyName, s_keyMap[i].keyName) == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const char *CSDLInputSystem::GetKeyName(int keyIndex)
|
|
{
|
|
if (keyIndex < 0 || keyIndex >= NUM_SDL_KEYS)
|
|
return nullptr;
|
|
return s_keyMap[keyIndex].keyName;
|
|
}
|
|
|
|
bool CSDLInputSystem::IsKeyPressed(int kbdNum, int keyIndex)
|
|
{
|
|
// Get SDL key for given index and check if currently pressed
|
|
SDL_Keycode sdlKey = s_keyMap[keyIndex].sdlKey;
|
|
return !!m_keyState[sdlKey];
|
|
}
|
|
|
|
int CSDLInputSystem::GetMouseAxisValue(int mseNum, int axisNum)
|
|
{
|
|
// Return value for given mouse axis
|
|
switch (axisNum)
|
|
{
|
|
case AXIS_X: return m_mouseX;
|
|
case AXIS_Y: return m_mouseY;
|
|
case AXIS_Z: return m_mouseZ;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
int CSDLInputSystem::GetMouseWheelDir(int mseNum)
|
|
{
|
|
// Return wheel value
|
|
return m_mouseWheelDir;
|
|
}
|
|
|
|
bool CSDLInputSystem::IsMouseButPressed(int mseNum, int butNum)
|
|
{
|
|
// Return value for given mouse button
|
|
switch (butNum)
|
|
{
|
|
case 0: return !!(m_mouseButtons & SDL_BUTTON_LMASK);
|
|
case 1: return !!(m_mouseButtons & SDL_BUTTON_MMASK);
|
|
case 2: return !!(m_mouseButtons & SDL_BUTTON_RMASK);
|
|
case 3: return !!(m_mouseButtons & SDL_BUTTON_X1MASK);
|
|
case 4: return !!(m_mouseButtons & SDL_BUTTON_X2MASK);
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
int CSDLInputSystem::GetJoyAxisValue(int joyNum, int axisNum)
|
|
{
|
|
// Get raw joystick axis value for given joystick from SDL (values range from -32768 to 32767)
|
|
SDL_Joystick *joystick = m_joysticks[joyNum];
|
|
return SDL_JoystickGetAxis(joystick, axisNum);
|
|
}
|
|
|
|
bool CSDLInputSystem::IsJoyPOVInDir(int joyNum, int povNum, int povDir)
|
|
{
|
|
// Get current joystick POV-hat value for given joystick and POV number from SDL and check if pointing in required direction
|
|
SDL_Joystick *joystick = m_joysticks[joyNum];
|
|
int hatVal = SDL_JoystickGetHat(joystick, povNum);
|
|
switch (povDir)
|
|
{
|
|
case POV_UP: return !!(hatVal & SDL_HAT_UP);
|
|
case POV_DOWN: return !!(hatVal & SDL_HAT_DOWN);
|
|
case POV_LEFT: return !!(hatVal & SDL_HAT_LEFT);
|
|
case POV_RIGHT: return !!(hatVal & SDL_HAT_RIGHT);
|
|
default: return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CSDLInputSystem::IsJoyButPressed(int joyNum, int butNum)
|
|
{
|
|
// Get current joystick button state for given joystick and button number from SDL
|
|
SDL_Joystick *joystick = m_joysticks[joyNum];
|
|
return !!SDL_JoystickGetButton(joystick, butNum);
|
|
}
|
|
|
|
bool CSDLInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd)
|
|
{
|
|
switch (ffCmd.id)
|
|
{
|
|
case FFStop:
|
|
StopAllEffect(joyNum);
|
|
break;
|
|
|
|
case FFConstantForce:
|
|
sdlConstForceMax = m_config["SDLConstForceMax"].ValueAs<unsigned>();
|
|
if (sdlConstForceMax == 0)
|
|
return false;
|
|
// note sr2 centering val=0.047244 and val=0.062992 alternatively (const value)
|
|
// max val between -1 to 1 (left right)
|
|
if (ffCmd.force == 0.0f)
|
|
StopConstanteforce(joyNum);
|
|
else if (ffCmd.force > 0.0f)
|
|
ConstantForceEffect(ffCmd.force * (float)(sdlConstForceMax / 100.0f), -1, SDL_HAPTIC_INFINITY, joyNum);
|
|
else if (ffCmd.force < 0.0f)
|
|
ConstantForceEffect(-ffCmd.force * (float)(sdlConstForceMax / 100.0f), 1, SDL_HAPTIC_INFINITY, joyNum);
|
|
break;
|
|
|
|
case FFSelfCenter:
|
|
sdlSelfCenterMax = m_config["SDLSelfCenterMax"].ValueAs<unsigned>();
|
|
if (sdlSelfCenterMax == 0)
|
|
return false;
|
|
SpringForceEffect(ffCmd.force * (float)(sdlSelfCenterMax / 100.0f), joyNum);
|
|
break;
|
|
|
|
case FFFriction:
|
|
sdlFrictionMax = m_config["SDLFrictionMax"].ValueAs<unsigned>();
|
|
if (sdlFrictionMax == 0)
|
|
return false;
|
|
FrictionForceEffect(ffCmd.force * (float)(sdlFrictionMax / 100.0f), joyNum);
|
|
break;
|
|
|
|
case FFVibrate:
|
|
sdlVibrateMax = m_config["SDLVibrateMax"].ValueAs<unsigned>();
|
|
if (sdlVibrateMax == 0)
|
|
return false;
|
|
VibrationEffect(ffCmd.force * (float)(sdlVibrateMax / 100.0f), joyNum);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int CSDLInputSystem::GetNumKeyboards()
|
|
{
|
|
// Return ANY_KEYBOARD as SDL 1.2 cannot handle multiple keyboards
|
|
return ANY_KEYBOARD;
|
|
}
|
|
|
|
int CSDLInputSystem::GetNumMice()
|
|
{
|
|
// Return ANY_MOUSE as SDL 1.2 cannot handle multiple mice
|
|
return ANY_MOUSE;
|
|
}
|
|
|
|
int CSDLInputSystem::GetNumJoysticks()
|
|
{
|
|
// Return number of joysticks found
|
|
return (int)m_joysticks.size();
|
|
}
|
|
|
|
const KeyDetails *CSDLInputSystem::GetKeyDetails(int kbdNum)
|
|
{
|
|
// Return nullptr as SDL 1.2 cannot handle multiple keyboards
|
|
return nullptr;
|
|
}
|
|
|
|
const MouseDetails *CSDLInputSystem::GetMouseDetails(int mseNum)
|
|
{
|
|
// Return nullptr as SDL 1.2 cannot handle multiple mice
|
|
return nullptr;
|
|
}
|
|
|
|
const JoyDetails *CSDLInputSystem::GetJoyDetails(int joyNum)
|
|
{
|
|
return &m_joyDetails[joyNum];
|
|
}
|
|
|
|
bool CSDLInputSystem::Poll()
|
|
{
|
|
// Reset mouse wheel direction
|
|
m_mouseWheelDir = 0;
|
|
|
|
// Poll for event from SDL
|
|
SDL_Event e;
|
|
while (SDL_PollEvent(&e))
|
|
{
|
|
switch (e.type)
|
|
{
|
|
default:
|
|
break;
|
|
case SDL_QUIT:
|
|
return false;
|
|
case SDL_MOUSEWHEEL:
|
|
if (e.button.y > 0)
|
|
{
|
|
m_mouseZ += 5;
|
|
m_mouseWheelDir = 1;
|
|
}
|
|
else if (e.button.y < 0)
|
|
{
|
|
m_mouseZ -= 5;
|
|
m_mouseWheelDir = -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get key state from SDL
|
|
m_keyState = SDL_GetKeyboardState(nullptr);
|
|
|
|
// Get mouse state from SDL (except mouse wheel which was handled earlier)
|
|
m_mouseButtons = SDL_GetMouseState(&m_mouseX, &m_mouseY);
|
|
|
|
// Update joystick state (not required as called implicitly by SDL_PollEvent above)
|
|
//SDL_JoystickUpdate();
|
|
return true;
|
|
}
|
|
|
|
void CSDLInputSystem::SetMouseVisibility(bool visible)
|
|
{
|
|
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
|
|
}
|
|
|
|
void CSDLInputSystem::StopAllEffect(int joyNum)
|
|
{
|
|
StopConstanteforce(joyNum);
|
|
StopVibrationforce(joyNum);
|
|
StopSpringforce(joyNum);
|
|
StopFrictionforce(joyNum);
|
|
}
|
|
|
|
void CSDLInputSystem::StopConstanteforce(int joyNum)
|
|
{
|
|
// stop constante effect or rumble constant effect
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_CONSTANT;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.constant.direction.dir[0] = 0;
|
|
eff.constant.length = 30;
|
|
eff.constant.delay = 0;
|
|
eff.constant.level = 0;
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectConstantForceID, &eff);
|
|
}
|
|
else
|
|
{
|
|
SDL_HapticRumbleStop(m_SDLHapticDatas[joyNum].SDLhaptic);
|
|
}
|
|
}
|
|
|
|
void CSDLInputSystem::StopVibrationforce(int joyNum)
|
|
{
|
|
// stop vibration-rumble effect
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_SINE;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.constant.length = 500;
|
|
eff.constant.delay = 0;
|
|
eff.periodic.period = 50;
|
|
eff.periodic.magnitude = 0;
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectVibrationID, &eff);
|
|
}
|
|
else
|
|
{
|
|
SDL_HapticRumbleStop(m_SDLHapticDatas[joyNum].SDLhaptic);
|
|
}
|
|
|
|
}
|
|
|
|
void CSDLInputSystem::StopSpringforce(int joyNum)
|
|
{
|
|
// stop spring effect
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_SPRING;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.condition.delay = 0;
|
|
eff.condition.length = SDL_HAPTIC_INFINITY;
|
|
eff.condition.left_sat[0] = 0xFFFF;
|
|
eff.condition.right_sat[0] = 0xFFFF;
|
|
eff.condition.left_coeff[0] = 0;
|
|
eff.condition.right_coeff[0] = 0;
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectSpringForceID, &eff);
|
|
}
|
|
|
|
}
|
|
|
|
void CSDLInputSystem::StopFrictionforce(int joyNum)
|
|
{
|
|
// stop friction effect
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_FRICTION;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.condition.delay = 0;
|
|
eff.condition.length = SDL_HAPTIC_INFINITY;
|
|
eff.condition.left_sat[0] = 0xFFFF;
|
|
eff.condition.right_sat[0] = 0xFFFF;
|
|
eff.condition.left_coeff[0] = 0;
|
|
eff.condition.right_coeff[0] = 0;
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectFrictionForceID, &eff);
|
|
}
|
|
|
|
}
|
|
|
|
void CSDLInputSystem::VibrationEffect(float strength, int joyNum)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_SINE;
|
|
eff.constant.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.periodic.delay = 0;
|
|
eff.periodic.length = SDL_HAPTIC_INFINITY;
|
|
eff.periodic.period = 50;
|
|
eff.periodic.magnitude = (int)(strength * INT16_MAX);
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectVibrationID, &eff);
|
|
|
|
SDL_HapticRunEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectVibrationID, 1);
|
|
}
|
|
else
|
|
{
|
|
if (strength != 0.0f)
|
|
SDL_HapticRumblePlay(m_SDLHapticDatas[joyNum].SDLhaptic, strength, 0);
|
|
else
|
|
SDL_HapticRumbleStop(m_SDLHapticDatas[joyNum].SDLhaptic);
|
|
}
|
|
|
|
}
|
|
|
|
void CSDLInputSystem::ConstantForceEffect(float force, int dir, int length, int joyNum)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_CONSTANT;
|
|
eff.constant.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.constant.direction.dir[0] = 0; // in cartesian mode dir on x set 0 on y set 1
|
|
eff.constant.length = length;
|
|
eff.constant.level = (int)(dir * (force * INT16_MAX));
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectConstantForceID, &eff);
|
|
|
|
SDL_HapticRunEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectConstantForceID, 1);
|
|
}
|
|
else
|
|
{
|
|
float threshold = (float)m_config["SDLConstForceThreshold"].ValueAs<unsigned>() / 100.0f;
|
|
if (force != 0.0f && force > threshold)
|
|
SDL_HapticRumblePlay(m_SDLHapticDatas[joyNum].SDLhaptic, force, 200);
|
|
else
|
|
SDL_HapticRumbleStop(m_SDLHapticDatas[joyNum].SDLhaptic);
|
|
}
|
|
|
|
}
|
|
|
|
void CSDLInputSystem::SpringForceEffect(float force, int joyNum)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
|
|
eff.type = SDL_HAPTIC_SPRING;
|
|
eff.constant.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.condition.delay = 0;
|
|
eff.condition.length = SDL_HAPTIC_INFINITY;
|
|
eff.condition.left_sat[0] = 0xffff;
|
|
eff.condition.right_sat[0] = 0xffff;
|
|
eff.condition.left_coeff[0] = (int)(force * INT16_MAX);
|
|
eff.condition.right_coeff[0] = (int)(force * INT16_MAX);
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectSpringForceID, &eff);
|
|
|
|
SDL_HapticRunEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectSpringForceID, 1);
|
|
}
|
|
|
|
}
|
|
|
|
void CSDLInputSystem::FrictionForceEffect(float force, int joyNum)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
|
|
eff.type = SDL_HAPTIC_FRICTION;
|
|
eff.constant.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.condition.delay = 0;
|
|
eff.condition.length = SDL_HAPTIC_INFINITY;
|
|
eff.condition.left_sat[0] = 0xffff;
|
|
eff.condition.right_sat[0] = 0xffff;
|
|
eff.condition.left_coeff[0] = (int)(force * INT16_MAX);
|
|
eff.condition.right_coeff[0] = (int)(force * INT16_MAX);
|
|
|
|
if (SDL_HapticEffectSupported(m_SDLHapticDatas[joyNum].SDLhaptic, &eff))
|
|
{
|
|
SDL_HapticUpdateEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectFrictionForceID, &eff);
|
|
|
|
SDL_HapticRunEffect(m_SDLHapticDatas[joyNum].SDLhaptic, m_SDLHapticDatas[joyNum].effectFrictionForceID, 1);
|
|
}
|
|
|
|
}
|
|
|
|
// due to the sdl2 SDL_HapticNumAxes() bug in linux (always return 2 in linux)
|
|
// test if haptic controller has the most basic constant force effect
|
|
// if it has -> ffb wheel or ffb joystick
|
|
// if it hasn't -> pad
|
|
bool CSDLInputSystem::HasBasicForce(SDL_Haptic* hap)
|
|
{
|
|
SDL_memset(&eff, 0, sizeof(SDL_HapticEffect));
|
|
eff.type = SDL_HAPTIC_CONSTANT;
|
|
eff.periodic.direction.type = SDL_HAPTIC_CARTESIAN;
|
|
eff.constant.direction.dir[0] = 0;
|
|
eff.constant.length = 30;
|
|
eff.constant.delay = 0;
|
|
eff.constant.level = 0;
|
|
|
|
if (SDL_HapticEffectSupported(hap, &eff))
|
|
return true;
|
|
else
|
|
return false;
|
|
} |