Use SDL2 for better gamepad support and hotplug

This commit is contained in:
Benau 2020-04-21 00:14:32 +08:00
parent cd7aa2c90c
commit 00cb6c2d48
22 changed files with 490 additions and 154 deletions

View File

@ -7,11 +7,14 @@ sudo: false
language: cpp
os:
- linux
# - osx
compiler:
- gcc
- clang
matrix:
# include:
# - osx_image: xcode11.3
fast_finish: true
env:
@ -36,6 +39,7 @@ addons:
- libogg-dev
- libopenal-dev
- libpng-dev
- libsdl2-dev
- libvorbis-dev
- libxrandr-dev
- mesa-common-dev

View File

@ -4,8 +4,8 @@ brew "libvorbis"
brew "openal-soft"
brew "freetype"
brew "curl"
brew "nettle"
brew "fribidi"
brew "glew"
brew "harfbuzz"
brew "libjpeg"
brew "sdl2"

View File

@ -296,6 +296,19 @@ if(NOT SERVER_ONLY)
endif()
endif()
if (NOT SERVER_ONLY)
# SDL2
find_library(SDL2_LIBRARY NAMES SDL2 libSDL2)
find_path(SDL2_INCLUDEDIR NAMES SDL.h PATH_SUFFIXES SDL2 include/SDL2 include PATHS)
if (NOT SDL2_LIBRARY OR NOT SDL2_INCLUDEDIR)
message(FATAL_ERROR "SDL2 not found. "
"SDL2 is required to handle gamepad in SuperTuxKart.")
else()
include_directories("${SDL2_INCLUDEDIR}")
MESSAGE(STATUS "Use system SDL2: ${SDL2_LIBRARY}")
endif()
endif()
# Build the irrlicht library
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/irrlicht")
include_directories("${PROJECT_SOURCE_DIR}/lib/irrlicht/include")
@ -404,7 +417,6 @@ if (NOT SERVER_ONLY)
include_directories("${RAQM_INCLUDEDIR}")
MESSAGE(STATUS "Use system libraqm: ${RAQM_LIBRARY}")
endif()
endif()
# OpenGL
@ -656,6 +668,7 @@ if(NOT SERVER_ONLY)
${FRIBIDI_LIBRARIES}
${FREETYPE_LIBRARIES}
${HARFBUZZ_LIBRARY}
${SDL2_LIBRARY}
graphics_utils)
endif()
@ -713,7 +726,7 @@ if(MSVC OR MINGW)
endif()
if(MINGW)
target_link_libraries(supertuxkart -ldxguid -ldinput8 -ldnsapi)
target_link_libraries(supertuxkart -ldxguid -ldnsapi)
if (NOT CMAKE_BUILD_TYPE MATCHES Debug)
target_link_libraries(supertuxkart -mwindows)
endif()

View File

@ -26,12 +26,13 @@ To build SuperTuxKart from source, you'll need to install the following packages
* libpng (libpng-devel)
* zlib (zlib-devel)
* jpeg (libjpeg-turbo-devel)
* SDL2 (libsdl2-devel)
Fedora command:
```bash
sudo dnf install @development-tools angelscript-devel \
bluez-libs-devel cmake desktop-file-utils \
bluez-libs-devel cmake desktop-file-utils SDL2-devel \
freealut-devel freeglut-devel freetype-devel fribidi-devel \
gcc-c++ git-core libXrandr-devel libcurl-devel libjpeg-turbo-devel \
libpng-devel libsquish-devel libtool libvorbis-devel mesa-libEGL-devel \
@ -44,7 +45,7 @@ Mageia 6 command:
```bash
su -c 'urpmi gcc-c++ cmake openssl-devel libcurl-devel freetype-devel harfbuzz-devel \
fribidi-devel libjpeg-turbo-devel libogg-devel openal-soft-devel \
fribidi-devel libjpeg-turbo-devel libogg-devel openal-soft-devel SDL2-devel \
libpng-devel libvorbis-devel nettle-devel zlib-devel git subversion \
mesa-comon-devel libxrandr-devel libbluez-devel libfreetype6-devel'
```
@ -52,7 +53,7 @@ mesa-comon-devel libxrandr-devel libbluez-devel libfreetype6-devel'
openSUSE command:
```bash
sudo zypper install gcc-c++ cmake openssl-devel libcurl-devel \
sudo zypper install gcc-c++ cmake openssl-devel libcurl-devel libSDL2-devel \
freetype-devel harfbuzz-devel fribidi-devel libogg-devel openal-soft-devel libpng-devel \
libvorbis-devel libXrandr-devel pkgconf zlib-devel enet-devel glew-devel \
libjpeg-devel bluez-devel freetype2-devel glu-devel
@ -61,7 +62,7 @@ libjpeg-devel bluez-devel freetype2-devel glu-devel
Ubuntu command:
```bash
sudo apt-get install build-essential cmake libbluetooth-dev \
sudo apt-get install build-essential cmake libbluetooth-dev libsdl2-devel \
libcurl4-openssl-dev libenet-dev libfreetype6-dev libharfbuzz-dev libfribidi-dev \
libgl1-mesa-dev libglew-dev libjpeg-dev libogg-dev libopenal-dev libpng-dev \
libssl-dev libvorbis-dev libxrandr-dev libx11-dev nettle-dev pkg-config zlib1g-dev

View File

@ -487,10 +487,6 @@ struct SEvent
NUMBER_OF_AXES = 32
};
/** A bitmap of button states. You can use IsButtonPressed() to
( check the state of each button from 0 to (NUMBER_OF_BUTTONS - 1) */
u32 ButtonStates;
/** For AXIS_X, AXIS_Y, AXIS_Z, AXIS_R, AXIS_U and AXIS_V
* Values are in the range -32768 to 32767, with 0 representing
* the center position. You will receive the raw value from the
@ -501,17 +497,28 @@ struct SEvent
*/
s16 Axis[NUMBER_OF_AXES];
/** The POV represents the angle of the POV hat in degrees * 100,
* from 0 to 35,900. A value of 65535 indicates that the POV hat
* is centered (or not present).
* This value is only supported on Windows. On Linux, the POV hat
* will be sent as 2 axes instead. */
u16 POV;
//! The ID of the joystick which generated this event.
/** This is an internal Irrlicht index; it does not map directly
* to any particular hardware joystick. */
u32 AxisChanged;
/** A bitmap of button states. You can use IsButtonPressed() to
( check the state of each button from 0 to (NUMBER_OF_BUTTONS - 1) */
u32 ButtonStates;
//! The ID of the joystick which generated this event.
/** This is an internal Irrlicht index; it does not map directly
* to any particular hardware joystick. */
u8 Joystick;
u32 Joystick;
//! A helper function to check if a button is pressed.
bool IsAxisChanged(u32 axis) const
{
if(axis >= (u32)NUMBER_OF_AXES)
return false;
return (AxisChanged & (1 << axis)) ? true : false;
}
//! A helper function to check if a button is pressed.
bool IsButtonPressed(u32 button) const

View File

@ -129,11 +129,7 @@
#endif
//! Define _IRR_COMPILE_WITH_JOYSTICK_SUPPORT_ if you want joystick events.
#define _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
#ifdef NO_IRR_COMPILE_WITH_JOYSTICK_EVENTS_
#undef _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
#endif
//! Maximum number of texture an SMaterial can have, up to 8 are supported by Irrlicht.
#define _IRR_MATERIAL_MAX_TEXTURES_ 8

View File

@ -66,6 +66,8 @@ extern bool GLContextDebugBit;
#define XRANDR_ROTATION_LEFT (1 << 1)
#define XRANDR_ROTATION_RIGHT (1 << 3)
#include <unistd.h>
namespace irr
{
namespace video

View File

@ -352,11 +352,10 @@ struct SJoystickWin32Control
void pollJoysticks()
{
if (0 == ActiveJoysticks.size())
return;
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
if (0 == ActiveJoysticks.size())
return;
u32 joystick;
DIJOYSTATE2 info;

View File

@ -31,19 +31,6 @@
#import <time.h>
#import "AppDelegate.h"
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#ifdef MACOS_10_0_4
#include <IOKit/hidsystem/IOHIDUsageTables.h>
#else
/* The header was moved here in Mac OS X 10.1 */
#include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
#endif
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h>
// only OSX 10.5 seems to not need these defines...
#if !defined(__MAC_10_5) || defined(__MAC_10_6)
// Contents from Events.h from Carbon/HIToolbox but we need it with Cocoa too
@ -185,6 +172,19 @@ enum {
};
#endif
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#ifdef MACOS_10_0_4
#include <IOKit/hidsystem/IOHIDUsageTables.h>
#else
/* The header was moved here in Mac OS X 10.1 */
#include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
#endif
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h>
struct JoystickComponent
{
IOHIDElementCookie cookie; // unique value which identifies element, will NOT change

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -144,6 +144,14 @@ public:
/** Returns if this config is sed by any devices. */
bool isPlugged() const { return m_plugged > 0; }
// ------------------------------------------------------------------------
/** Decrease ref counter. */
void unPlugged()
{
m_plugged--;
assert(m_plugged >= 0);
}
// ------------------------------------------------------------------------
/** Returns the number of devices using this configuration. */
int getNumberOfDevices() const { return m_plugged; }

View File

@ -58,8 +58,6 @@ DeviceManager::~DeviceManager()
// -----------------------------------------------------------------------------
bool DeviceManager::initialize()
{
GamepadConfig *gamepadConfig = NULL;
GamePadDevice *gamepadDevice = NULL;
m_map_fire_to_select = false;
bool created = false;
@ -110,65 +108,6 @@ bool DeviceManager::initialize()
m_keyboards.push_back(new KeyboardDevice(m_keyboard_configs.get(n)));
}
if(UserConfigParams::logMisc())
Log::info("Device manager","Initializing gamepad support.");
irr_driver->getDevice()->activateJoysticks(m_irrlicht_gamepads);
int num_gamepads = m_irrlicht_gamepads.size();
if(UserConfigParams::logMisc())
{
Log::info("Device manager","Irrlicht reports %d gamepads are attached to the system.",
num_gamepads);
}
// Create GamePadDevice for each physical gamepad and find a GamepadConfig to match
for (int id = 0; id < num_gamepads; id++)
{
core::stringc name = m_irrlicht_gamepads[id].Name;
// Some systems report a disk accelerometer as a gamepad, skip that
if (name.find("LIS3LV02DL") != -1) continue;
if (name == "applesmc") continue;
if (name.find("VirtualBox") == 0) continue;
if(m_irrlicht_gamepads[id].HasGenericName)
{
// On Windows all gamepads are given the same name ('microsoft
// pc-joystick driver'). Irrlicht now tries to get a better name
// from the registry, but in case this should fail we still have
// all gamepads with the same name shown in the GUI. This makes
// configuration totally useless, so append an ID to the name.
name = name + " " + StringUtils::toString(id).c_str();
}
if (UserConfigParams::logMisc())
{
Log::info("Device manager","#%d: %s detected...", id, name.c_str());
}
// Returns true if new configuration was created
if (getConfigForGamepad(id, name.c_str(), &gamepadConfig) == true)
{
if(UserConfigParams::logMisc())
Log::info("Device manager","creating new configuration.");
created = true;
}
else
{
if(UserConfigParams::logMisc())
Log::info("Device manager","using existing configuration.");
}
gamepadConfig->setPlugged();
gamepadDevice = new GamePadDevice(id,
name.c_str(),
m_irrlicht_gamepads[id].Axes,
m_irrlicht_gamepads[id].Buttons,
gamepadConfig );
addGamepad(gamepadDevice);
} // end for
if ((UserConfigParams::m_multitouch_active == 1 &&
irr_driver->getDevice()->supportsTouchDevice()) ||
UserConfigParams::m_multitouch_active > 1)
@ -268,20 +207,9 @@ bool DeviceManager::getConfigForGamepad(const int irr_id,
// If we can't find an appropriate configuration then create one.
if (!found)
{
if(irr_id < (int)(m_irrlicht_gamepads.size()))
{
*config = new GamepadConfig( name,
m_irrlicht_gamepads[irr_id].Axes,
m_irrlicht_gamepads[irr_id].Buttons );
}
#ifdef ENABLE_WIIUSE
else // Wiimotes have a higher ID and do not refer to m_irrlicht_gamepads
{
// The Wiimote manager will set number of buttons and axis
*config = new GamepadConfig(name.c_str());
}
#endif
// The Wiimote manager and SDL controller will set number of buttons and
// axes
*config = new GamepadConfig(name.c_str());
// Add new config to list
m_gamepad_configs.push_back(*config);
configCreated = true;

View File

@ -69,8 +69,6 @@ private:
PtrVector<GamepadConfig, HOLD> m_gamepad_configs;
MultitouchDevice* m_multitouch_device;
/** The list of all joysticks that were found and activated. */
core::array<SJoystickInfo> m_irrlicht_gamepads;
InputDevice* m_latest_used_device;
PlayerAssignMode m_assign_mode;

View File

@ -40,20 +40,9 @@ GamePadDevice::GamePadDevice(const int irr_index, const std::string &name,
config->setNumberOfButtons(button_count);
}
// HAT/POV buttons will be reported as additional axis with the values
// HAT_V_ID > HAT_H_ID. So increase the number of axis to be large
// enough to handle HAT_V/H_ID as axis number.
assert(Input::HAT_V_ID > Input::HAT_H_ID);
int adj_axis_count = axis_count > Input::HAT_V_ID ? axis_count
: Input::HAT_V_ID+1;
if(m_configuration->getNumberOfAxes()<adj_axis_count)
{
config->setNumberOfAxis(adj_axis_count);
}
m_prev_axis_directions.resize(adj_axis_count);
m_prev_axis_value.resize(adj_axis_count);
m_axis_ok.resize(adj_axis_count);
m_prev_axis_directions.resize(axis_count);
m_prev_axis_value.resize(axis_count);
m_axis_ok.resize(axis_count);
m_irr_index = irr_index;
m_name = name;

View File

@ -73,6 +73,7 @@ public:
int getIrrIndex() const { return m_irr_index; }
// ------------------------------------------------------------------------
void setIrrIndex(int i ) { m_irr_index = i; }
}; // class GamepadDevice

View File

@ -29,6 +29,7 @@
InputDevice::InputDevice()
{
m_connected = true;
m_player = NULL;
m_configuration = NULL;
} // InputDevice

View File

@ -47,6 +47,9 @@ enum DeviceType
class InputDevice: public NoCopy
{
protected:
/** For SDL controller it's set false when it's unplugged. */
bool m_connected;
/** Device type (keyboard, gamepad). */
DeviceType m_type;
@ -107,6 +110,10 @@ public:
// ------------------------------------------------------------------------
/** Returns the name of this device. */
const std::string& getName() const { return m_name; }
// ------------------------------------------------------------------------
void setConnected(bool val) { m_connected = val; }
// ------------------------------------------------------------------------
bool isConnected() const { return m_connected; }
}; // class InputDevice
#endif

View File

@ -31,6 +31,7 @@
#include "input/input.hpp"
#include "input/keyboard_device.hpp"
#include "input/multitouch_device.hpp"
#include "input/sdl_controller.hpp"
#include "input/wiimote_manager.hpp"
#include "karts/controller/controller.hpp"
#include "karts/abstract_kart.hpp"
@ -62,6 +63,9 @@
#include <sstream>
#include <algorithm>
#ifndef SERVER_ONLY
#include <SDL.h>
#endif
InputManager *input_manager;
@ -83,7 +87,14 @@ InputManager::InputManager() : m_mode(BOOTSTRAP),
m_timer_in_use = false;
m_master_player_only = false;
m_timer = 0;
#ifndef SERVER_ONLY
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0)
{
Log::error("InputManager", "Unable to initialize SDL: %s",
SDL_GetError());
}
#endif
}
// -----------------------------------------------------------------------------
void InputManager::update(float dt)
@ -93,6 +104,65 @@ void InputManager::update(float dt)
wiimote_manager->update();
#endif
#ifndef SERVER_ONLY
SDL_Event event;
while (SDL_PollEvent(&event))
{
try
{
switch (event.type)
{
case SDL_QUIT:
{
exit(-1);
break;
}
case SDL_JOYDEVICEADDED:
{
std::unique_ptr<SDLController> c(
new SDLController(event.jdevice.which));
SDL_JoystickID id = c->getInstanceID();
m_sdl_controller[id] = std::move(c);
break;
}
case SDL_JOYDEVICEREMOVED:
{
m_sdl_controller.erase(event.jdevice.which);
break;
}
case SDL_JOYAXISMOTION:
{
auto& controller = m_sdl_controller.at(event.jaxis.which);
if (controller->handleAxis(event))
input(controller->getEvent());
break;
}
case SDL_JOYHATMOTION:
{
auto& controller = m_sdl_controller.at(event.jhat.which);
if (controller->handleHat(event))
input(controller->getEvent());
break;
}
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
{
auto& controller = m_sdl_controller.at(event.jbutton.which);
if (controller->handleButton(event))
input(controller->getEvent());
break;
}
default:
break;
}
}
catch (std::exception& e)
{
Log::error("SDLController", "%s", e.what());
}
}
#endif
if(m_timer_in_use)
{
m_timer -= dt;
@ -105,6 +175,11 @@ void InputManager::update(float dt)
*/
InputManager::~InputManager()
{
#ifndef SERVER_ONLY
m_sdl_controller.clear();
SDL_Quit();
#endif
delete m_device_manager;
} // ~InputManager
@ -952,11 +1027,11 @@ EventPropagation InputManager::input(const SEvent& event)
const float ORIENTATION_MULTIPLIER = 10.0f;
if (event.EventType == EET_JOYSTICK_INPUT_EVENT)
{
// Axes - FIXME, instead of checking all of them, ask the bindings
// which ones to poll
for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ;
axis_id++)
{
if (!event.JoystickEvent.IsAxisChanged(axis_id))
continue;
int value = event.JoystickEvent.Axis[axis_id];
if (UserConfigParams::m_gamepad_debug)
@ -970,26 +1045,6 @@ EventPropagation InputManager::input(const SEvent& event)
axis_id, Input::AD_NEUTRAL, value);
}
if (event.JoystickEvent.POV == 65535)
{
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_H_ID, Input::AD_NEUTRAL, 0);
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_V_ID, Input::AD_NEUTRAL, 0);
}
else
{
// *0.017453925f is to convert degrees to radians
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_H_ID, Input::AD_NEUTRAL,
(int)(cosf(event.JoystickEvent.POV*0.017453925f/100.0f)
*Input::MAX_VALUE));
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_V_ID, Input::AD_NEUTRAL,
(int)(sinf(event.JoystickEvent.POV*0.017453925f/100.0f)
*Input::MAX_VALUE));
}
GamePadDevice* gp =
getDeviceManager()->getGamePadFromIrrID(event.JoystickEvent.Joystick);

View File

@ -20,6 +20,8 @@
#ifndef HEADER_INPUT_MANAGER_HPP
#define HEADER_INPUT_MANAGER_HPP
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <set>
@ -29,6 +31,7 @@
#include "utils/no_copy.hpp"
class DeviceManager;
class SDLController;
/**
* \brief Class to handle input.
@ -74,6 +77,11 @@ private:
void handleStaticAction(int id0, int value);
void inputSensing(Input::InputType type, int deviceID, int btnID,
Input::AxisDirection axisDirection, int value);
#ifndef SERVER_ONLY
std::map<int, std::unique_ptr<SDLController> > m_sdl_controller;
#endif
public:
InputManager();
~InputManager();
@ -101,6 +109,14 @@ public:
/** Returns the ID of the player that plays with the keyboard,
* or -1 if none. */
int getPlayerKeyboardID() const;
#ifdef SERVER_ONLY
size_t getGamepadCount() const { return 0; }
#else
/** Returns number of active connected gamepad (with SDL), notice the
* disconnected gamepad will not be removed from device manager to allow
* re-plugging later with the same ID. */
size_t getGamepadCount() const { return m_sdl_controller.size(); }
#endif
};
extern InputManager *input_manager;

View File

@ -0,0 +1,180 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2020 SuperTuxKart-Team
//
// This program 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.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef SERVER_ONLY
#include "input/sdl_controller.hpp"
#include "input/device_config.hpp"
#include "input/device_manager.hpp"
#include "input/gamepad_device.hpp"
#include "input/input_manager.hpp"
#include "utils/log.hpp"
#include <stdexcept>
#include <string>
// ----------------------------------------------------------------------------
SDLController::SDLController(int device_id)
: m_gamepad(NULL)
{
m_irr_event = {};
m_irr_event.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
m_game_controller = NULL;
m_joystick = NULL;
m_id = -1;
if (SDL_IsGameController(device_id))
{
m_game_controller = SDL_GameControllerOpen(device_id);
if (!m_game_controller)
throw std::runtime_error(SDL_GetError());
m_joystick = SDL_GameControllerGetJoystick(m_game_controller);
if (!m_joystick)
{
SDL_GameControllerClose(m_game_controller);
throw std::runtime_error(SDL_GetError());
}
}
else
{
m_joystick = SDL_JoystickOpen(device_id);
if (!m_joystick)
throw std::runtime_error(SDL_GetError());
}
m_id = SDL_JoystickInstanceID(m_joystick);
if (m_id < 0)
{
if (m_game_controller)
SDL_GameControllerClose(m_game_controller);
else
SDL_JoystickClose(m_joystick);
throw std::runtime_error(SDL_GetError());
}
m_irr_event.JoystickEvent.Joystick = m_id;
const char* name_cstr = SDL_JoystickName(m_joystick);
if (name_cstr == NULL)
{
if (m_game_controller)
SDL_GameControllerClose(m_game_controller);
else
SDL_JoystickClose(m_joystick);
throw std::runtime_error("missing name for joystick");
}
std::string name = name_cstr;
m_buttons = SDL_JoystickNumButtons(m_joystick);
if (m_buttons < 0)
{
if (m_game_controller)
SDL_GameControllerClose(m_game_controller);
else
SDL_JoystickClose(m_joystick);
throw std::runtime_error(SDL_GetError());
}
m_axes = SDL_JoystickNumAxes(m_joystick);
if (m_axes < 0)
{
if (m_game_controller)
SDL_GameControllerClose(m_game_controller);
else
SDL_JoystickClose(m_joystick);
throw std::runtime_error(SDL_GetError());
}
m_hats = SDL_JoystickNumHats(m_joystick);
if (m_hats < 0)
{
if (m_game_controller)
SDL_GameControllerClose(m_game_controller);
else
SDL_JoystickClose(m_joystick);
throw std::runtime_error(SDL_GetError());
}
Log::info("SDLController",
"%s plugged in: buttons: %d, axes: %d, hats: %d.", name.c_str(),
m_buttons, m_axes, m_hats);
if (m_game_controller && SDL_GameControllerName(m_game_controller))
{
Log::info("SDLController", "%s uses game controller mapping %s.",
name.c_str(), SDL_GameControllerName(m_game_controller));
}
if (m_buttons > irr::SEvent::SJoystickEvent::NUMBER_OF_BUTTONS)
m_buttons = irr::SEvent::SJoystickEvent::NUMBER_OF_BUTTONS;
if (m_axes > irr::SEvent::SJoystickEvent::NUMBER_OF_AXES)
m_axes = irr::SEvent::SJoystickEvent::NUMBER_OF_AXES;
// We store hats event with 4 buttons
int max_buttons_with_hats =
irr::SEvent::SJoystickEvent::NUMBER_OF_BUTTONS - (m_hats * 4);
if (m_buttons > max_buttons_with_hats)
m_hats = 0;
else
m_buttons += m_hats * 4;
DeviceManager* dm = input_manager->getDeviceManager();
GamepadConfig* cfg = NULL;
bool created = false;
if (dm->getConfigForGamepad(m_id, name, &cfg) == true)
{
Log::info("SDLController", "creating new configuration for %s.",
name.c_str());
created = true;
}
cfg->setNumberOfButtons(m_buttons);
cfg->setNumberOfAxis(m_axes);
cfg->setPlugged();
for (int i = 0; i < dm->getGamePadAmount(); i++)
{
GamePadDevice* d = dm->getGamePad(i);
if (d->getName() == name && !d->isConnected())
{
m_gamepad = d;
d->setConnected(true);
d->setIrrIndex(m_id);
d->setConfiguration(cfg);
if (created)
dm->save();
return;
}
}
m_gamepad = new GamePadDevice(m_id, name, m_axes, m_buttons, cfg);
dm->addGamepad(m_gamepad);
if (created)
dm->save();
} // SDLController
// ----------------------------------------------------------------------------
SDLController::~SDLController()
{
Log::info("SDLController", "%s unplugged.", SDL_JoystickName(m_joystick));
if (m_game_controller)
SDL_GameControllerClose(m_game_controller);
else
SDL_JoystickClose(m_joystick);
m_gamepad->getConfiguration()->unPlugged();
m_gamepad->setIrrIndex(-1);
m_gamepad->setConnected(false);
} // ~SDLController
#endif

View File

@ -0,0 +1,131 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2020 SuperTuxKart-Team
//
// This program 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.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_SDL_CONTROLLER_HPP
#define HEADER_SDL_CONTROLLER_HPP
#ifndef SERVER_ONLY
#include <SDL.h>
#include <IEventReceiver.h>
#include "utils/types.hpp"
class GamePadDevice;
class SDLController
{
private:
SDL_GameController* m_game_controller;
SDL_Joystick* m_joystick;
GamePadDevice* m_gamepad;
int m_buttons;
int m_axes;
int m_hats;
SDL_JoystickID m_id;
irr::SEvent m_irr_event;
public:
// ------------------------------------------------------------------------
SDLController(int device_id);
// ------------------------------------------------------------------------
~SDLController();
// ------------------------------------------------------------------------
const irr::SEvent& getEvent() const { return m_irr_event; }
// ------------------------------------------------------------------------
SDL_JoystickID getInstanceID() const { return m_id; }
// ------------------------------------------------------------------------
bool handleAxis(const SDL_Event& event)
{
if (event.jaxis.axis > m_axes)
return false;
m_irr_event.JoystickEvent.Axis[event.jaxis.axis] = event.jaxis.value;
uint32_t value = 1 << event.jaxis.axis;
m_irr_event.JoystickEvent.AxisChanged = value;
return true;
} // handleAxis
// ------------------------------------------------------------------------
bool handleHat(const SDL_Event& event)
{
if (event.jhat.hat > m_hats)
return false;
uint32_t value = 0;
// Up, right, down and left (4 buttons)
switch (event.jhat.value)
{
case SDL_HAT_UP:
value = 1;
break;
case SDL_HAT_RIGHTUP:
value = 1 | (1 << 1);
break;
case SDL_HAT_RIGHT:
value = 1 << 1;
break;
case SDL_HAT_RIGHTDOWN:
value = (1 << 1) | (1 << 2);
break;
case SDL_HAT_DOWN:
value = 1 << 2;
break;
case SDL_HAT_LEFTDOWN:
value = (1 << 2) | (1 << 3);
break;
case SDL_HAT_LEFT:
value = 1 << 3;
break;
case SDL_HAT_LEFTUP:
value = (1 << 3) | 1;
break;
case SDL_HAT_CENTERED:
default:
value = 0;
break;
}
int hat_start = m_buttons - (m_hats * 4);
unsigned hat_mask = (unsigned)((1 << hat_start) - 1);
m_irr_event.JoystickEvent.ButtonStates &= hat_mask;
value <<= hat_start;
value <<= (m_hats - 1) * 4;
m_irr_event.JoystickEvent.ButtonStates |= value;
m_irr_event.JoystickEvent.AxisChanged = 0;
return true;
} // handleHat
// ------------------------------------------------------------------------
bool handleButton(const SDL_Event& event)
{
if (event.jbutton.button > m_buttons)
return false;
bool pressed = event.jbutton.state == SDL_PRESSED;
uint32_t value = 1 << event.jbutton.button;
if (pressed)
m_irr_event.JoystickEvent.ButtonStates |= value;
else
m_irr_event.JoystickEvent.ButtonStates &= (uint32_t)~value;
m_irr_event.JoystickEvent.AxisChanged = 0;
return true;
} // handleButton
};
#endif
#endif

View File

@ -68,8 +68,8 @@ void Wiimote::resetIrrEvent()
event.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
for(int i=0 ; i < irr::SEvent::SJoystickEvent::NUMBER_OF_AXES ; i++)
event.JoystickEvent.Axis[i] = 0;
event.JoystickEvent.AxisChanged = 1;
event.JoystickEvent.Joystick = getIrrId();
event.JoystickEvent.POV = 65535;
event.JoystickEvent.ButtonStates = 0;
} // resetIrrEvent