Browse Source

Multiple controllers support (#935)

pull/948/head
Gleb Mazovetskiy 5 years ago committed by GitHub
parent
commit
b10b4381ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      SourceS/sdl2_to_1_2_backports.h
  2. 5
      SourceX/DiabloUI/diabloui.cpp
  3. 48
      SourceX/controls/controller.cpp
  4. 4
      SourceX/controls/controller.h
  5. 6
      SourceX/controls/controller_motion.cpp
  6. 107
      SourceX/controls/devices/game_controller.cpp
  7. 32
      SourceX/controls/devices/game_controller.h
  8. 138
      SourceX/controls/devices/joystick.cpp
  9. 45
      SourceX/controls/devices/joystick.h
  10. 7
      SourceX/controls/game_controls.cpp
  11. 12
      SourceX/display.cpp
  12. 12
      SourceX/miniwin/misc_msg.cpp

1
SourceS/sdl2_to_1_2_backports.h

@ -48,6 +48,7 @@
// For now we only process ASCII input when using SDL1.
#define SDL_TEXTINPUTEVENT_TEXT_SIZE 2
#define SDL_JoystickID Sint32
#define SDL_JoystickNameForIndex SDL_JoystickName
inline void SDL_Log(const char *fmt, ...)

5
SourceX/DiabloUI/diabloui.cpp

@ -357,10 +357,7 @@ void UiHandleEvents(SDL_Event *event)
diablo_quit(0);
#ifndef USE_SDL1
if (event->type == SDL_JOYDEVICEADDED || event->type == SDL_JOYDEVICEREMOVED) {
InitController();
return;
}
HandleControllerAddedOrRemovedEvent(*event);
if (event->type == SDL_WINDOWEVENT) {
if (event->window.event == SDL_WINDOWEVENT_SHOWN)

48
SourceX/controls/controller.cpp

@ -8,7 +8,7 @@ namespace dvl {
ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event)
{
ControllerButtonEvent result{ ControllerButton_NONE, false };
ControllerButtonEvent result { ControllerButton_NONE, false };
switch (event.type) {
#ifndef USE_SDL1
case SDL_CONTROLLERBUTTONUP:
@ -28,34 +28,56 @@ ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event)
#endif
#ifndef USE_SDL1
result.button = GameControllerToControllerButton(event);
if (result.button != ControllerButton_NONE)
return result;
GameController *const controller = GameController::Get(event);
if (controller != NULL) {
result.button = controller->ToControllerButton(event);
if (result.button != ControllerButton_NONE)
return result;
}
#endif
result.button = JoyButtonToControllerButton(event);
const Joystick *joystick = Joystick::Get(event);
if (joystick != NULL)
result.button = joystick->ToControllerButton(event);
return result;
}
bool IsControllerButtonPressed(ControllerButton button)
{
bool result = false;
#ifndef USE_SDL1
result = result || IsGameControllerButtonPressed(button);
if (GameController::IsPressedOnAnyController(button))
return true;
#endif
#if HAS_KBCTRL == 1
result = result || IsKbCtrlButtonPressed(button);
if (IsKbCtrlButtonPressed(button))
return true;
#endif
result = result || IsJoystickButtonPressed(button);
return result;
return Joystick::IsPressedOnAnyJoystick(button);
}
void InitController()
bool HandleControllerAddedOrRemovedEvent(const SDL_Event &event)
{
InitJoystick();
#ifndef USE_SDL1
InitGameController();
switch (event.type) {
case SDL_CONTROLLERDEVICEADDED:
GameController::Add(event.cdevice.which);
break;
case SDL_CONTROLLERDEVICEREMOVED:
GameController::Remove(event.cdevice.which);
break;
case SDL_JOYDEVICEADDED:
Joystick::Add(event.jdevice.which);
break;
case SDL_JOYDEVICEREMOVED:
Joystick::Remove(event.jdevice.which);
break;
default:
return false;
}
return true;
#else
return false;
#endif
}

4
SourceX/controls/controller.h

@ -1,5 +1,7 @@
#pragma once
#include <SDL.h>
#include "controls/controller_buttons.h"
namespace dvl {
@ -15,6 +17,6 @@ ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event);
bool IsControllerButtonPressed(ControllerButton button);
void InitController();
bool HandleControllerAddedOrRemovedEvent(const SDL_Event &event);
} // namespace dvl

6
SourceX/controls/controller_motion.cpp

@ -123,12 +123,14 @@ void ScaleJoysticks()
bool ProcessControllerMotion(const SDL_Event &event, ControllerButtonEvent ctrl_event)
{
#ifndef USE_SDL1
if (ProcessGameControllerAxisMotion(event)) {
GameController *const controller = GameController::Get(event);
if (controller != NULL && controller->ProcessAxisMotion(event)) {
ScaleJoysticks();
return true;
}
#endif
if (ProcessJoystickAxisMotion(event)) {
Joystick *const joystick = Joystick::Get(event);
if (joystick != NULL && joystick->ProcessAxisMotion(event)) {
ScaleJoysticks();
return true;
}

107
SourceX/controls/devices/game_controller.cpp

@ -1,36 +1,37 @@
#include "controls/devices/game_controller.h"
#ifndef USE_SDL1
#include <cstddef>
#include "controls/controller_motion.h"
#include "controls/devices/joystick.h"
#include "stubs.h"
namespace dvl {
static SDL_GameController *current_game_controller = NULL;
static bool sgbTriggerLeftDown = false;
static bool sgbTriggerRightDown = false;
std::vector<GameController> *const GameController::controllers_ = new std::vector<GameController>;
ControllerButton GameControllerToControllerButton(const SDL_Event &event)
ControllerButton GameController::ToControllerButton(const SDL_Event &event)
{
switch (event.type) {
case SDL_CONTROLLERAXISMOTION:
switch (event.caxis.axis) {
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
if (event.caxis.value < 8192) { // 25% pressed
sgbTriggerLeftDown = false;
trigger_left_is_down_ = false;
}
if (event.caxis.value > 16384 && !sgbTriggerLeftDown) { // 50% pressed
sgbTriggerLeftDown = true;
if (event.caxis.value > 16384 && !trigger_left_is_down_) { // 50% pressed
trigger_left_is_down_ = true;
return ControllerButton_AXIS_TRIGGERLEFT;
}
return ControllerButton_NONE;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
if (event.caxis.value < 8192) { // 25% pressed
sgbTriggerRightDown = false;
trigger_right_is_down_ = false;
}
if (event.caxis.value > 16384 && !sgbTriggerRightDown) { // 50% pressed
sgbTriggerRightDown = true;
if (event.caxis.value > 16384 && !trigger_right_is_down_) { // 50% pressed
trigger_right_is_down_ = true;
return ControllerButton_AXIS_TRIGGERRIGHT;
}
return ControllerButton_NONE;
@ -76,9 +77,7 @@ ControllerButton GameControllerToControllerButton(const SDL_Event &event)
return ControllerButton_NONE;
}
namespace {
SDL_GameControllerButton ControllerButtonToGameControllerButton(ControllerButton button)
SDL_GameControllerButton GameController::ToSdlGameControllerButton(ControllerButton button) const
{
if (button == ControllerButton_AXIS_TRIGGERLEFT || button == ControllerButton_AXIS_TRIGGERRIGHT)
UNIMPLEMENTED();
@ -116,17 +115,13 @@ SDL_GameControllerButton ControllerButtonToGameControllerButton(ControllerButton
}
}
} // namespace
bool IsGameControllerButtonPressed(ControllerButton button)
bool GameController::IsPressed(ControllerButton button) const
{
if (current_game_controller == NULL)
return false;
const SDL_GameControllerButton gc_button = ControllerButtonToGameControllerButton(button);
return gc_button != SDL_CONTROLLER_BUTTON_INVALID && SDL_GameControllerGetButton(current_game_controller, gc_button);
const SDL_GameControllerButton gc_button = ToSdlGameControllerButton(button);
return gc_button != SDL_CONTROLLER_BUTTON_INVALID && SDL_GameControllerGetButton(sdl_game_controller_, gc_button);
}
bool ProcessGameControllerAxisMotion(const SDL_Event &event)
bool GameController::ProcessAxisMotion(const SDL_Event &event)
{
if (event.type != SDL_CONTROLLERAXISMOTION)
return false;
@ -153,20 +148,72 @@ bool ProcessGameControllerAxisMotion(const SDL_Event &event)
return true;
}
SDL_GameController *CurrentGameController()
void GameController::Add(int joystick_index)
{
return current_game_controller;
SDL_Log("Opening game controller for joystick at index %d", joystick_index);
GameController result;
result.sdl_game_controller_ = SDL_GameControllerOpen(joystick_index);
if (result.sdl_game_controller_ == NULL) {
SDL_Log(SDL_GetError());
SDL_ClearError();
return;
}
SDL_Joystick *const sdl_joystick = SDL_GameControllerGetJoystick(result.sdl_game_controller_);
result.instance_id_ = SDL_JoystickInstanceID(sdl_joystick);
controllers_->push_back(result);
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(sdl_joystick);
SDL_Log("Opened game controller with mapping:\n%s", SDL_GameControllerMappingForGUID(guid));
}
void InitGameController()
void GameController::Remove(SDL_JoystickID instance_id)
{
if (CurrentJoystickIndex() == -1)
SDL_Log("Removing game controller with instance id %d", instance_id);
for (std::size_t i = 0; i < controllers_->size(); ++i) {
const GameController &controller = (*controllers_)[i];
if (controller.instance_id_ != instance_id)
continue;
controllers_->erase(controllers_->begin() + i);
sgbControllerActive = !controllers_->empty();
return;
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(CurrentJoystick());
SDL_Log("Opening gamepad %d: %s", CurrentJoystickIndex(), SDL_GameControllerMappingForGUID(guid));
current_game_controller = SDL_GameControllerOpen(CurrentJoystickIndex());
if (current_game_controller == NULL)
SDL_Log(SDL_GetError());
}
SDL_Log("Game controller not found with instance id: %d", instance_id);
}
GameController *GameController::Get(SDL_JoystickID instance_id)
{
for (std::size_t i = 0; i < controllers_->size(); ++i) {
GameController &controller = (*controllers_)[i];
if (controller.instance_id_ == instance_id)
return &controller;
}
return NULL;
}
GameController *GameController::Get(const SDL_Event &event)
{
switch (event.type) {
case SDL_CONTROLLERAXISMOTION:
return Get(event.caxis.which);
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
return Get(event.jball.which);
default:
return NULL;
}
}
const std::vector<GameController> &GameController::All()
{
return *controllers_;
}
bool GameController::IsPressedOnAnyController(ControllerButton button)
{
for (std::size_t i = 0; i < controllers_->size(); ++i)
if ((*controllers_)[i].IsPressed(button))
return true;
return false;
}
} // namespace dvl

32
SourceX/controls/devices/game_controller.h

@ -1,22 +1,42 @@
#pragma once
#include <vector>
#include <SDL.h>
#include "controls/controller_buttons.h"
#ifndef USE_SDL1
namespace dvl {
ControllerButton GameControllerToControllerButton(const SDL_Event &event);
class GameController {
static std::vector<GameController> *const controllers_;
public:
static void Add(int joystick_index);
static void Remove(SDL_JoystickID instance_id);
static GameController *Get(SDL_JoystickID instance_id);
static GameController *Get(const SDL_Event &event);
static const std::vector<GameController> &All();
static bool IsPressedOnAnyController(ControllerButton button);
// NOTE: Not idempotent.
// Must be called exactly once for each SDL input event.
ControllerButton ToControllerButton(const SDL_Event &event);
bool IsGameControllerButtonPressed(ControllerButton button);
bool IsPressed(ControllerButton button) const;
bool ProcessAxisMotion(const SDL_Event &event);
bool ProcessGameControllerAxisMotion(const SDL_Event &event);
private:
SDL_GameControllerButton ToSdlGameControllerButton(ControllerButton button) const;
SDL_GameController *CurrentGameController();
SDL_GameController *sdl_game_controller_ = NULL;
SDL_JoystickID instance_id_ = -1;
// Must be called after InitJoystick().
void InitGameController();
bool trigger_left_is_down_ = false;
bool trigger_right_is_down_ = false;
};
} // namespace dvl
#endif

138
SourceX/controls/devices/joystick.cpp

@ -1,12 +1,16 @@
#include "controls/devices/joystick.h"
#include <cstddef>
#include "controls/controller_motion.h"
#include "stubs.h"
namespace dvl {
ControllerButton JoyButtonToControllerButton(const SDL_Event &event)
std::vector<Joystick> *const Joystick::joysticks_ = new std::vector<Joystick>;
ControllerButton Joystick::ToControllerButton(const SDL_Event &event) const
{
switch (event.type) {
case SDL_JOYBUTTONDOWN:
@ -105,9 +109,7 @@ ControllerButton JoyButtonToControllerButton(const SDL_Event &event)
return ControllerButton_NONE;
}
namespace {
int JoyButtonToControllerButton(ControllerButton button)
int Joystick::ToSdlJoyButton(ControllerButton button) const
{
if (button == ControllerButton_AXIS_TRIGGERLEFT || button == ControllerButton_AXIS_TRIGGERRIGHT)
UNIMPLEMENTED();
@ -173,43 +175,41 @@ int JoyButtonToControllerButton(ControllerButton button)
}
}
bool IsJoystickHatButtonPressed(ControllerButton button)
bool Joystick::IsHatButtonPressed(ControllerButton button) const
{
switch (button) {
#if defined(JOY_HAT_DPAD_UP_HAT) && defined(JOY_HAT_DPAD_UP)
case ControllerButton_BUTTON_DPAD_UP:
return (SDL_JoystickGetHat(CurrentJoystick(), JOY_HAT_DPAD_UP_HAT) & JOY_HAT_DPAD_UP) != 0;
return (SDL_JoystickGetHat(sdl_joystick_, JOY_HAT_DPAD_UP_HAT) & JOY_HAT_DPAD_UP) != 0;
#endif
#if defined(JOY_HAT_DPAD_DOWN_HAT) && defined(JOY_HAT_DPAD_DOWN)
case ControllerButton_BUTTON_DPAD_DOWN:
return (SDL_JoystickGetHat(CurrentJoystick(), JOY_HAT_DPAD_DOWN_HAT) & JOY_HAT_DPAD_DOWN) != 0;
return (SDL_JoystickGetHat(sdl_joystick_, JOY_HAT_DPAD_DOWN_HAT) & JOY_HAT_DPAD_DOWN) != 0;
#endif
#if defined(JOY_HAT_DPAD_LEFT_HAT) && defined(JOY_HAT_DPAD_LEFT)
case ControllerButton_BUTTON_DPAD_LEFT:
return (SDL_JoystickGetHat(CurrentJoystick(), JOY_HAT_DPAD_LEFT_HAT) & JOY_HAT_DPAD_LEFT) != 0;
return (SDL_JoystickGetHat(sdl_joystick_, JOY_HAT_DPAD_LEFT_HAT) & JOY_HAT_DPAD_LEFT) != 0;
#endif
#if defined(JOY_HAT_DPAD_RIGHT_HAT) && defined(JOY_HAT_DPAD_RIGHT)
case ControllerButton_BUTTON_DPAD_RIGHT:
return (SDL_JoystickGetHat(CurrentJoystick(), JOY_HAT_DPAD_RIGHT_HAT) & JOY_HAT_DPAD_RIGHT) != 0;
return (SDL_JoystickGetHat(sdl_joystick_, JOY_HAT_DPAD_RIGHT_HAT) & JOY_HAT_DPAD_RIGHT) != 0;
#endif
default:
return false;
}
}
} // namespace
bool IsJoystickButtonPressed(ControllerButton button)
bool Joystick::IsPressed(ControllerButton button) const
{
if (CurrentJoystick() == NULL)
if (sdl_joystick_ == NULL)
return false;
if (IsJoystickHatButtonPressed(button))
if (IsHatButtonPressed(button))
return true;
const int joy_button = JoyButtonToControllerButton(button);
return joy_button != -1 && SDL_JoystickGetButton(CurrentJoystick(), joy_button);
const int joy_button = ToSdlJoyButton(button);
return joy_button != -1 && SDL_JoystickGetButton(sdl_joystick_, joy_button);
}
bool ProcessJoystickAxisMotion(const SDL_Event &event)
bool Joystick::ProcessAxisMotion(const SDL_Event &event)
{
if (event.type != SDL_JOYAXISMOTION)
return false;
@ -244,50 +244,92 @@ bool ProcessJoystickAxisMotion(const SDL_Event &event)
return true;
}
static SDL_Joystick *current_joystick = NULL;
SDL_Joystick *CurrentJoystick()
void Joystick::Add(int device_index)
{
return current_joystick;
if (SDL_NumJoysticks() <= device_index)
return;
Joystick result;
SDL_Log("Adding joystick %d: %s", device_index,
SDL_JoystickNameForIndex(device_index));
result.sdl_joystick_ = SDL_JoystickOpen(device_index);
if (result.sdl_joystick_ == NULL) {
SDL_Log(SDL_GetError());
SDL_ClearError();
return;
}
#ifndef USE_SDL1
result.instance_id_ = SDL_JoystickInstanceID(result.sdl_joystick_);
#endif
joysticks_->push_back(result);
sgbControllerActive = true;
}
static int current_joystick_index = -1;
int CurrentJoystickIndex()
void Joystick::Remove(SDL_JoystickID instance_id)
{
return current_joystick_index;
#ifndef USE_SDL1
SDL_Log("Removing joystick (instance id: %d)", instance_id);
for (std::size_t i = 0; i < joysticks_->size(); ++i) {
const Joystick &joystick = (*joysticks_)[i];
if (joystick.instance_id_ != instance_id)
continue;
joysticks_->erase(joysticks_->begin() + i);
sgbControllerActive = !joysticks_->empty();
return;
}
SDL_Log("Joystick not found with instance id: %d", instance_id);
#endif
}
void InitJoystick()
const std::vector<Joystick> &Joystick::All()
{
#if HAS_KBCTRL == 1
sgbControllerActive = true;
#endif
return *joysticks_;
}
if (SDL_NumJoysticks() == 0) {
current_joystick_index = -1;
#if HAS_KBCTRL == 0
sgbControllerActive = false;
#endif
return;
Joystick *Joystick::Get(SDL_JoystickID instance_id)
{
for (std::size_t i = 0; i < joysticks_->size(); ++i) {
Joystick &joystick = (*joysticks_)[i];
if (joystick.instance_id_ == instance_id)
return &joystick;
}
return NULL;
}
// Get the first available controller.
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
Joystick *Joystick::Get(const SDL_Event &event)
{
switch (event.type) {
#ifndef USE_SDL1
if (!SDL_IsGameController(i))
continue;
case SDL_JOYAXISMOTION:
return Get(event.jaxis.which);
case SDL_JOYBALLMOTION:
return Get(event.jball.which);
case SDL_JOYHATMOTION:
return Get(event.jhat.which);
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
return Get(event.jbutton.which);
return Get(event.jbutton.which);
default:
return NULL;
#else
case SDL_JOYAXISMOTION:
case SDL_JOYBALLMOTION:
case SDL_JOYHATMOTION:
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
return joysticks_->empty() ? NULL : &(*joysticks_)[0];
default:
return NULL;
#endif
SDL_Log("Initializing joystick %d: %s", i, SDL_JoystickNameForIndex(i));
current_joystick = SDL_JoystickOpen(i);
if (current_joystick == NULL) {
SDL_Log(SDL_GetError());
continue;
}
current_joystick_index = i;
sgbControllerActive = true;
break;
}
}
bool Joystick::IsPressedOnAnyJoystick(ControllerButton button)
{
for (std::size_t i = 0; i < joysticks_->size(); ++i)
if ((*joysticks_)[i].IsPressed(button))
return true;
return false;
}
} // namespace dvl

45
SourceX/controls/devices/joystick.h

@ -1,21 +1,44 @@
#pragma once
// Joystick mappings for SDL1 and additional buttons on SDL2.
#include <SDL.h>
#include "controls/controller_buttons.h"
namespace dvl {
#include <vector>
ControllerButton JoyButtonToControllerButton(const SDL_Event &event);
#include <SDL.h>
bool IsJoystickButtonPressed(ControllerButton button);
#ifdef USE_SDL1
#include "sdl2_to_1_2_backports.h"
#endif
bool ProcessJoystickAxisMotion(const SDL_Event &event);
#include "controls/controller_buttons.h"
SDL_Joystick *CurrentJoystick();
int CurrentJoystickIndex();
namespace dvl {
void InitJoystick();
class Joystick {
static std::vector<Joystick> *const joysticks_;
public:
static void Add(int device_index);
static void Remove(SDL_JoystickID instance_id);
static Joystick *Get(SDL_JoystickID instance_id);
static Joystick *Get(const SDL_Event &event);
static const std::vector<Joystick> &All();
static bool IsPressedOnAnyJoystick(ControllerButton button);
ControllerButton ToControllerButton(const SDL_Event &event) const;
bool IsPressed(ControllerButton button) const;
bool ProcessAxisMotion(const SDL_Event &event);
SDL_JoystickID instance_id() const
{
return instance_id_;
}
private:
int ToSdlJoyButton(ControllerButton button) const;
bool IsHatButtonPressed(ControllerButton button) const;
SDL_Joystick *sdl_joystick_ = NULL;
SDL_JoystickID instance_id_ = -1;
};
} // namespace dvl

7
SourceX/controls/game_controls.cpp

@ -272,9 +272,10 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrl_event, Gam
}
#ifndef USE_SDL1
// Ignore unhandled joystick events if gamepad is active.
// We receive the same events as gamepad events.
if (CurrentGameController() != NULL && event.type >= SDL_JOYAXISMOTION && event.type <= SDL_JOYBUTTONUP) {
// Ignore unhandled joystick events where a GameController is open for this joystick.
// This is because SDL sends both game controller and joystick events in this case.
const Joystick *const joystick = Joystick::Get(event);
if (joystick != NULL && GameController::Get(joystick->instance_id()) != NULL) {
return true;
}
if (event.type == SDL_CONTROLLERAXISMOTION) {

12
SourceX/display.cpp

@ -1,6 +1,8 @@
#include "display.h"
#include "DiabloUI/diabloui.h"
#include "controls/controller.h"
#include "controls/devices/game_controller.h"
#include "controls/devices/joystick.h"
#ifdef USE_SDL1
#ifndef SDL1_VIDEO_MODE_BPP
@ -121,8 +123,14 @@ bool SpawnWindow(const char *lpWindowName)
#ifdef USE_SDL1
SDL_EnableUNICODE(1);
#endif
#if defined(USE_SDL1) || defined(__SWITCH__)
InitController();
#ifdef USE_SDL1
// On SDL 1, there are no ADDED/REMOVED events.
// Always try to initialize the first joystick.
Joystick::Add(0);
#ifdef __SWITCH__
// TODO: There is a bug in SDL2 on Switch where it does not repport controllers on startup (Jan 1, 2020)
GameController::Add(0);
#endif
#endif
int width = DEFAULT_WIDTH;

12
SourceX/miniwin/misc_msg.cpp

@ -337,6 +337,9 @@ bool PeekMessage(LPMSG lpMsg)
return true;
}
if (HandleControllerAddedOrRemovedEvent(e))
return true;
const ControllerButtonEvent ctrl_event = ToControllerButtonEvent(e);
if (ProcessControllerMotion(e, ctrl_event))
return true;
@ -448,15 +451,6 @@ bool PeekMessage(LPMSG lpMsg)
}
switch (e.type) {
#ifndef USE_SDL1
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
break;
case SDL_JOYDEVICEADDED:
case SDL_JOYDEVICEREMOVED:
InitController();
break;
#endif
case SDL_QUIT:
lpMsg->message = DVL_WM_QUIT;
break;

Loading…
Cancel
Save