cpp-sdl2
C++ header-only SDL2 wrapper
game_controller.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "exception.hpp"
4 #include "haptic.hpp"
5 #include <SDL.h>
6 #include <SDL_gamecontroller.h>
7 #include <cassert>
8 #include <chrono>
9 #include <string>
10 
11 namespace sdl
12 {
15 {
16 public:
18  GameController(int joystick_index) : controller_(SDL_GameControllerOpen(joystick_index))
19  {
20  if (!controller_)
21  {
22  throw Exception("SDL_GameControllerOpen");
23  }
24  }
25 
27  GameController(SDL_GameController* controller) : controller_(controller) {}
28 
30  GameController() = default;
31 
33  GameController(GameController const&) = delete;
34  GameController& operator=(GameController const&) = delete;
35 
37  GameController(GameController&& other) noexcept { *this = std::move(other); }
38 
41  {
42  if (controller_ != other.controller_)
43  {
44  controller_ = other.ptr();
45  // We never want the value of the "owned_" boolean to change in the
46  // life of one of these objects. however, in case of a move
47  // operation, we need to transfer the owning status of the wrapper
48  // In the general case, we would only want to rely on the fact that
49  // the pointer is null or not. But here, we want to be able to
50  // easily construct and manipulate a pointer that is not managed as
51  // an RAAI object. This is ugly, and I'm upset about it. But we're
52  // gonna cast-away const for once
53  *(const_cast<bool*>(&owned_)) = other.owned_;
54  *(const_cast<bool*>(&other.owned_)) = false;
55  other.controller_ = nullptr;
56  }
57  return *this;
58  }
59 
62  {
63  if (owned_ && controller_) SDL_GameControllerClose(controller_);
64  }
65 
67  SDL_GameController* ptr() const { return controller_; }
68 
70  Haptic open_haptic() const { return {SDL_GameControllerGetJoystick(controller_)}; }
71 
73  bool is_attached() const { return SDL_GameControllerGetAttached(controller_) == SDL_TRUE; }
74 
76  int16_t get_axis(SDL_GameControllerAxis axis) const
77  {
78  return SDL_GameControllerGetAxis(controller_, axis);
79  }
80 
82  int8_t get_button(SDL_GameControllerButton button) const
83  {
84  return SDL_GameControllerGetButton(controller_, button);
85  }
86 
87 #if SDL_VERSION_ATLEAST(2, 0, 9)
88  int rumble(uint16_t low_freq, uint16_t high_freq, std::chrono::milliseconds duration) const
90  {
91  return rumble(low_freq, high_freq, static_cast<uint32_t>(duration.count()));
92  }
93 
95  int rumble(uint16_t low_freq, uint16_t high_freq, uint32_t millisec_duration) const
96  {
97  return SDL_GameControllerRumble(controller_, low_freq, high_freq, millisec_duration);
98  }
99 #endif
100 
101  std::string name() const
102  {
103  if (!controller_) return {};
104 
105  return {SDL_GameControllerName(controller_)};
106  }
107 
108  static std::string get_controller_name(int joystick_index)
109  {
110  const char* name = SDL_GameControllerNameForIndex(joystick_index);
111  if (!name) throw Exception("SDL_GameControllerNameForIndex");
112  return {name};
113  }
114 
115  // static std::string get_axis_name(SDL_GameControllerAxis axis)
116  //{
117  // //todo error when SDL return null
118  // return { SDL_GameControllerGetStringForAxis(axis) };
119  //}
120 
121  // static std::string get_button_name(SDL_GameControllerButton button)
122  //{
123  // //todo error when SDL return null
124  // return { SDL_GameControllerGetStringForButton(button) };
125  //}
126 
127  // Bindings management wrappers
129  static int load_mapping_database(std::string const& file_path)
130  {
131  return load_mapping_database(file_path.c_str());
132  }
133 
135  static int load_mapping_database(const char* file_path)
136  {
137  const auto state = SDL_GameControllerAddMappingsFromFile(file_path);
138  if (state < 0)
139  {
140  throw Exception("SDL_GameControllerAddMappingsFromFile");
141  }
142 
143  return state;
144  }
145 
147  static int add_mapping(std::string const& mapping_string)
148  {
149  return add_mapping(mapping_string.c_str());
150  }
151 
153  static int add_mapping(const char* mapping_string)
154  {
155  const auto state = SDL_GameControllerAddMapping(mapping_string);
156 
157  if (state < 0)
158  {
159  throw Exception("SDL_GameControllerAddMapping");
160  }
161 
162  return state;
163  }
164 
165  // convinience functions
167  static std::vector<GameController> open_all_available_controllers()
168  {
169  std::vector<GameController> controllers;
170 
171  for (int nb_sticks = SDL_NumJoysticks(), i = 0; i < nb_sticks; ++i)
172  {
173  if (SDL_IsGameController(i))
174  {
175  try
176  {
177  controllers.emplace_back(i);
178  }
179  catch (sdl::Exception const& /*e*/)
180  {
181  // could not open this controller
182  continue;
183  }
184  }
185  }
186 
187  return controllers;
188  }
189 
191  static GameController non_owning(SDL_JoystickID joystick_id)
192  {
193  return {SDL_GameControllerFromInstanceID(joystick_id), false};
194  }
195 
197  static GameController non_owning(SDL_GameController* controller) { return {controller, false}; }
198 
199 private:
201  GameController(SDL_GameController* controller, bool non_owned)
202  : controller_(controller), owned_(non_owned)
203  {
204  assert(!owned_);
205  }
206 
207  SDL_GameController* controller_ = nullptr;
208  bool const owned_ = true;
209 };
210 } // namespace sdl
int8_t get_button(SDL_GameControllerButton button) const
Get the current imedate value of the given button.
static int load_mapping_database(std::string const &file_path)
Load a file database.
static std::string get_controller_name(int joystick_index)
GameController(int joystick_index)
Construct a controller from a joystick index, throws if that index is not a game controller.
GameController(GameController &&other) noexcept
move ctor
int16_t get_axis(SDL_GameControllerAxis axis) const
Get the current imediate value of the given axis.
Haptic open_haptic() const
Open the haptic device from the controller.
static GameController non_owning(SDL_JoystickID joystick_id)
Create a non_owning controller around a stick ID, to use the C++ API without managing the controller...
SDL_GameController * controller_
static std::vector< GameController > open_all_available_controllers()
Try to open all available controllers, and return an array of all controller sucessfully openned...
int rumble(uint16_t low_freq, uint16_t high_freq, uint32_t millisec_duration) const
Play a simple rumble. If the controller has 2 motors, the two values will control one of them...
Define to deactivate exception support.
Definition: color.hpp:7
GameController & operator=(GameController const &)=delete
std::string name() const
SDL_GameController * ptr() const
Get the SDL pointer.
static int add_mapping(std::string const &mapping_string)
Add a mapping string.
~GameController()
Close the opened controller pointer, unless this wrapper was created via GameController::non_owning()...
static int load_mapping_database(const char *file_path)
Load a file database.
GameController()=default
Construct an empty controller.
GameController(SDL_GameController *controller)
Construct a controller from a GameController pointer. This object will take ownership of the controll...
bool is_attached() const
Return true if this controller is attached.
GameController & operator=(GameController &&other) noexcept
move-assing operator
static int add_mapping(const char *mapping_string)
Add a mapping string.
GameController(SDL_GameController *controller, bool non_owned)
Private controller for a non-onwer controller. The bool argument is expected to be false here...
static GameController non_owning(SDL_GameController *controller)
Create a non_owning controller around an SDL controller pointer, to use the C++ aPI withiout managing...
Define to deactivate exception support.
Definition: exception.hpp:21
state
Power states.
Definition: utils.hpp:148
int rumble(uint16_t low_freq, uint16_t high_freq, std::chrono::milliseconds duration) const
Play a simple rumble. If the controller has 2 motors, the two values will control one of them...
Represent a gamepad (game controller)