Supermodel/Src/OSD/SDL/SDLInputSystem.cpp
joachim 5fa190490d Allow joystick to be fetch if windows has no focus in SDL input mode.
This is usefull for multiple instance of supermodel networked on the same machine.
2023-03-09 10:34:51 -08:00

836 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 "SDLInputSystem.h"
#include "Supermodel.h"
#include "Inputs/Input.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;
//allow joystick to be fetch if windows has no focus.
//this is usefull for multiple instance networked on the same machine
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
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;
}