FIFE  be64c707dea6b3250bd4355bf5c825d25920087d
joystickmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2019 by the FIFE team *
3  * http://www.fifengine.net *
4  * This file is part of FIFE. *
5  * *
6  * FIFE is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU Lesser General Public *
8  * License as published by the Free Software Foundation; either *
9  * version 2.1 of the License, or (at your option) any later version. *
10  * *
11  * This library is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14  * Lesser General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU Lesser General Public *
17  * License along with this library; if not, write to the *
18  * Free Software Foundation, Inc., *
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 #include <iostream>
24 
25 // 3rd party library includes
26 #include <SDL.h>
27 
28 // FIFE includes
29 // These includes are split up in two parts, separated by one empty line
30 // First block: files included from the FIFE root src directory
31 // Second block: files included from the same folder
32 //#include "loaders/native/input/controllermappingloader.h"
33 //#include "savers/native/input/controllermappingsaver.h"
34 #include "util/base/exception.h"
35 #include "util/log/logger.h"
36 #include "util/math/fife_math.h"
37 
38 #include "joystickmanager.h"
39 
40 namespace FIFE {
41  static Logger _log(LM_EVTCHANNEL);
42 
44  // init joystick and controller systems
45  if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
46  throw SDLException(SDL_GetError());
47  }
48  // create loader/saver for controller mappings
51 
52  // add already connected joysticks / controllers
53  for (int32_t i = 0; i < SDL_NumJoysticks(); ++i) {
54  addJoystick(i);
55  }
56  // enable joystick and gamecontroller events
57  SDL_JoystickEventState(SDL_ENABLE);
58  SDL_GameControllerEventState(SDL_ENABLE);
59  }
60 
62  for (std::vector<Joystick*>::iterator it = m_joysticks.begin(); it != m_joysticks.end(); ++it) {
63  delete *it;
64  }
65 
66  SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
67  }
68 
69  Joystick* JoystickManager::addJoystick(int32_t deviceIndex) {
70  Joystick* joystick = NULL;
71  for (std::vector<Joystick*>::iterator it = m_activeJoysticks.begin(); it != m_activeJoysticks.end(); ++it) {
72  if ((*it)->getDeviceIndex() == deviceIndex) {
73  return joystick;
74  }
75  }
76  std::string guidStr = getGuidString(deviceIndex);
77  for (std::vector<Joystick*>::iterator it = m_joysticks.begin(); it != m_joysticks.end(); ++it) {
78  if (!(*it)->isConnected() && (*it)->getGuid() == guidStr) {
79  joystick = *it;
80  break;
81  }
82  }
83  if (!joystick) {
84  joystick = new Joystick(m_joysticks.size(), deviceIndex);
85  m_joysticks.push_back(joystick);
86  } else {
87  joystick->setDeviceIndex(deviceIndex);
88  }
89  joystick->open();
90  addControllerGuid(joystick);
91  m_joystickIndices.insert(std::pair<int32_t, uint32_t>(joystick->getInstanceId(), joystick->getJoystickId()));
92  m_activeJoysticks.push_back(joystick);
93  return joystick;
94  }
95 
96  Joystick* JoystickManager::getJoystick(int32_t instanceId) {
97  Joystick* joy = NULL;
98  std::map<int32_t, uint32_t>::iterator it = m_joystickIndices.find(instanceId);
99  if (it != m_joystickIndices.end()) {
100  joy = m_joysticks[it->second];
101  }
102  return joy;
103  }
104 
106  std::vector<Joystick*>::iterator it = std::find(m_activeJoysticks.begin(), m_activeJoysticks.end(), joystick);
107  if (it != m_activeJoysticks.end()) {
108  m_joystickIndices.erase((*it)->getInstanceId());
110  (*it)->close();
111  m_activeJoysticks.erase(it);
112  }
113  }
114 
116  return static_cast<uint8_t>(m_activeJoysticks.size());
117  }
118 
119  void JoystickManager::loadMapping(const std::string& file) {
120  m_mappingLoader.load(file);
121  // check if one of the joysticks can now be opened as gamecontroller
122  for (std::vector<Joystick*>::iterator it = m_activeJoysticks.begin(); it != m_activeJoysticks.end(); ++it) {
123  if (!(*it)->isController()) {
124  (*it)->openController();
125  addControllerGuid(*it);
126  }
127  }
128  }
129 
130  void JoystickManager::saveMapping(const std::string guid, const std::string& file) {
131  std::string stringMapping = getStringMapping(guid);
132  m_mappingSaver.save(stringMapping, file);
133  }
134 
135  void JoystickManager::saveMappings(const std::string& file) {
136  std::string stringMappings;
137  std::map<std::string, uint8_t>::iterator it = m_gamepadGuids.begin();
138  for (; it != m_gamepadGuids.end(); ++it) {
139  stringMappings += getStringMapping(it->first);
140  }
141  m_mappingSaver.save(stringMappings, file);
142  }
143 
144  std::string JoystickManager::getStringMapping(const std::string& guid) {
145  SDL_JoystickGUID realGuid = SDL_JoystickGetGUIDFromString(guid.c_str());
146  char* mapping = SDL_GameControllerMappingForGUID(realGuid);
147  if (!mapping) {
148  throw SDLException(SDL_GetError());
149  return std::string();
150  }
151 
152  std::string stringMapping(mapping);
153  SDL_free(mapping);
154  // add missing platform if needed
155  if (stringMapping.find_last_of(',') != stringMapping.length() - 1) {
156  stringMapping += ",";
157  }
158  std::size_t platPos = stringMapping.find("platform:");
159  if (platPos == std::string::npos) {
160  stringMapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n";
161  }
162  return stringMapping;
163  }
164 
165  void JoystickManager::setStringMapping(const std::string& mapping) {
166  int32_t result = SDL_GameControllerAddMapping(mapping.c_str());
167  if (result == 1) {
168  // check if one of the joysticks can now be opened as gamecontroller
169  for (std::vector<Joystick*>::iterator it = m_activeJoysticks.begin(); it != m_activeJoysticks.end(); ++it) {
170  if (!(*it)->isController()) {
171  (*it)->openController();
172  addControllerGuid(*it);
173  }
174  }
175  } else if (result == -1) {
176  throw SDLException(SDL_GetError());
177  }
178  }
179 
181  m_joystickListeners.push_back(listener);
182  }
183 
185  m_joystickListeners.push_front(listener);
186  }
187 
189  if (listener->isActive()) {
190  listener->setActive(false);
191  for (std::deque<IJoystickListener*>::iterator it = m_joystickListeners.begin(); it != m_joystickListeners.end(); ++it) {
192  if (*it == listener) {
193  m_joystickListeners.erase(it);
194  break;
195  }
196  }
197  }
198  }
199 
201  bool dispatch = true;
202  JoystickEvent joyevt;
203  joyevt.setSource(this);
204 
205  if (event.type == SDL_JOYAXISMOTION) {
207  joyevt.setInstanceId(event.jaxis.which);
208  joyevt.setAxis(event.jaxis.axis);
209  joyevt.setAxisValue(convertRange(event.jaxis.value));
210  } else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP) {
211  joyevt.setType(event.type == SDL_JOYBUTTONDOWN ? JoystickEvent::BUTTON_PRESSED : JoystickEvent::BUTTON_RELEASED);
212  joyevt.setInstanceId(event.jbutton.which);
213  joyevt.setButton(event.jbutton.button);
214  } else if (event.type == SDL_JOYHATMOTION) {
216  joyevt.setInstanceId(event.jhat.which);
217  joyevt.setHat(event.jhat.hat);
218  joyevt.setHatValue(event.jhat.value);
219  } else if (event.type == SDL_JOYDEVICEADDED) {
221  // Note: In this case it's the device index, instead of instance id
222  Joystick* joy = addJoystick(event.jdevice.which);
223  if (joy) {
224  joyevt.setInstanceId(joy->getInstanceId());
225  } else {
226  dispatch = false;
227  }
228  } else if (event.type == SDL_JOYDEVICEREMOVED) {
230  joyevt.setInstanceId(event.jdevice.which);
231  } else {
232  dispatch = false;
233  }
234  // Dispatch only if it's not a controller, SDL sends events twice.
235  // Only exception for added and removed events.
236  Joystick* joy = getJoystick(joyevt.getInstanceId());
237  dispatch = dispatch && (!joy->isController() || (event.type == SDL_JOYDEVICEREMOVED || event.type == SDL_JOYDEVICEADDED));
238  if (dispatch) {
239  joyevt.setController(joy->isController());
240  dispatchJoystickEvent(joyevt);
241  }
242  // Remove it after event dispatch.
243  if (event.type == SDL_JOYDEVICEREMOVED) {
244  removeJoystick(joy);
245  }
246  }
247 
249  bool dispatch = true;
250  JoystickEvent joyevt;
251  joyevt.setSource(this);
252  joyevt.setController(true);
253 
254  if (event.type == SDL_CONTROLLERAXISMOTION) {
256  joyevt.setInstanceId(event.caxis.which);
257  joyevt.setAxis(event.caxis.axis);
258  joyevt.setAxisValue(convertRange(event.caxis.value));
259  } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) {
260  joyevt.setType(event.type == SDL_CONTROLLERBUTTONDOWN ? JoystickEvent::BUTTON_PRESSED : JoystickEvent::BUTTON_RELEASED);
261  joyevt.setInstanceId(event.cbutton.which);
262  joyevt.setButton(event.cbutton.button);
263  } else {
264  dispatch = false;
265  }
266 
267  if (dispatch) {
268  dispatchJoystickEvent(joyevt);
269  }
270  }
271 
272 
274  std::deque<IJoystickListener*> listeners = m_joystickListeners;
275  std::deque<IJoystickListener*>::iterator i = listeners.begin();
276  for (; i != listeners.end(); ++i) {
277  if (!(*i)->isActive()) continue;
278  switch (evt.getType()) {
280  (*i)->axisMotion(evt);
281  break;
283  (*i)->hatMotion(evt);
284  break;
286  (*i)->buttonPressed(evt);
287  break;
289  (*i)->buttonReleased(evt);
290  break;
292  (*i)->deviceAdded(evt);
293  break;
295  (*i)->deviceRemoved(evt);
296  break;
297  default:
298  break;
299  }
300  if (evt.isConsumed()) {
301  break;
302  }
303  }
304  }
305 
307  return ES_ENGINE;
308  }
309 
310  std::string JoystickManager::getGuidString(int32_t deviceIndex) {
311  char tmp[33];
312  SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(deviceIndex);
313  SDL_JoystickGetGUIDString(guid, tmp, sizeof(tmp));
314  std::string guidString(tmp);
315  return guidString;
316  }
317 
318  float JoystickManager::convertRange(int16_t value) {
319  float range = static_cast<float>(value) / 32768.0f;
320  if (Mathf::FAbs(range) < 0.01f) {
321  return 0.0f;
322  }
323  if (range < -0.99f) {
324  return -1.0f;
325  } else if (range > 0.99f) {
326  return 1.0f;
327  }
328  return range;
329  }
330 
332  if (!joystick->isController()) {
333  return;
334  }
335  std::pair<std::map<std::string, uint8_t>::iterator, bool> ret;
336  ret = m_gamepadGuids.insert(std::pair<std::string, uint8_t>(joystick->getGuid(), 1));
337  if (ret.second == false) {
338  ++ret.first->second;
339  }
340  }
341 
343  if (!joystick->isController()) {
344  return;
345  }
346  std::map<std::string, uint8_t>::iterator it = m_gamepadGuids.find(joystick->getGuid());
347  if (it != m_gamepadGuids.end()) {
348  --it->second;
349  }
350  }
351 }
void addJoystickListener(IJoystickListener *listener)
Adds a listener to the back of the listener deque Listener will be notified via the corresponding eve...
JoystickManager()
Constructor.
void save(const std::string data, const std::string &filename)
Saves mapping to file.
virtual ~JoystickManager()
Destructor.
void removeJoystick(Joystick *joystick)
Removes the given joystick, can be reused.
void processJoystickEvent(SDL_Event event)
Creates and process joystick events.
ControllerMappingLoader m_mappingLoader
Loader for gamecontroller mapping.
uint8_t getJoystickCount() const
Return the number of joysticks / gamecontrollers.
virtual bool isActive()
Indicates if the listener is active.
Definition: ilistener.h:44
float convertRange(int16_t value)
Converts the int16 in -1.0 to 1.0 range.
void removeControllerGuid(Joystick *joystick)
Removes / decal controller GUID.
virtual bool isConsumed() const
Checks whether event is consumed.
bool isController() const
Indicates if this a controller.
Definition: joystick.cpp:119
static Logger _log(LM_AUDIO)
std::map< std::string, uint8_t > m_gamepadGuids
Each sort of gamepad have a GUID from SDL. Indicates the number of gamepads with given GUID are conne...
void setStringMapping(const std::string &mapping)
Sets controller mapping from string and adds or updates the related controllers.
void setHat(int8_t hat)
Sets the hat index.
std::vector< Joystick * > m_joysticks
All "known" Joysticks. Useful if a user reconnect a Joystick.
void setAxis(int8_t axis)
Sets the axis index number or the Joystick::ControllerAxis.
Definition: joystickevent.h:96
void setDeviceIndex(int32_t deviceIndex)
Sets the device index of the joystick.
Definition: joystick.cpp:64
Listener of joystick events.
std::vector< Joystick * > m_activeJoysticks
All active / connected Joysticks.
Represents a Joystick and if available the Gamecontroller.
Definition: joystick.h:39
unsigned char uint8_t
Definition: core.h:38
void addControllerGuid(Joystick *joystick)
Adds GUID from controller.
std::map< int32_t, uint32_t > m_joystickIndices
Map to hold the relation between Joystick InstanceId and JoystickId.
void setType(JoystickEventType type)
Sets the event type.
Definition: joystickevent.h:80
void dispatchJoystickEvent(JoystickEvent &evt)
Dispatches joystick / controller events.
void setInstanceId(int32_t id)
Sets the instance id of the joystick.
Definition: joystickevent.h:88
const std::string & getGuid()
Return the GUID of the joystick / gamecontroller class as string.
Definition: joystick.cpp:72
ControllerMappingSaver m_mappingSaver
Saver for gamecontroller mapping.
int32_t getJoystickId() const
Sets the instance id of the joystick.
Definition: joystick.cpp:60
void load(const std::string &filename)
Loads mapping from file.
void setController(bool value)
Sets to true if the event is for a controller, otherwise false.
void saveMapping(const std::string guid, const std::string &file)
Saves controller mapping for given GUID in the specified file.
void processControllerEvent(SDL_Event event)
Creates and process gamecontroller events.
std::string getGuidString(int32_t deviceIndex)
Return GUID for given device index as string.
Joystick * getJoystick(int32_t instanceId)
Return the joystick with the given instance id.
void loadMapping(const std::string &file)
Loads controller mappings from given file and if possible, it opens the related controllers.
void addJoystickListenerFront(IJoystickListener *listener)
Adds a listener to the front of the listener deque Listener will be notified via the corresponding ev...
Class for Joystick events.
Definition: joystickevent.h:45
std::string getStringMapping(const std::string &guid)
Return the controller mapping for given GUID as string.
void removeJoystickListener(IJoystickListener *listener)
Removes an added listener from the controller.
Joystick * addJoystick(int32_t deviceIndex)
Adds a joystick with the given device index.
void setButton(int8_t button)
Sets the button index or Joystick::ControllerButton.
void saveMappings(const std::string &file)
Saves all controller mappings that were used during the season.
EventSourceType
Types for different event sources.
JoystickEventType getType() const
Return the event type.
Definition: joystickevent.h:76
void setHatValue(int8_t value)
Sets the hat value.
void setAxisValue(float value)
Sets the axis value.
static T FAbs(T _val)
Definition: fife_math.h:227
int32_t getInstanceId() const
Return the instance id of the joystick.
Definition: joystickevent.h:84
int32_t getInstanceId() const
Return the instance id of the joystick.
Definition: joystick.cpp:56
void open()
Opens / activates the joystick and sets values.
Definition: joystick.cpp:80
virtual void setSource(IEventSource *source)
Sets the source of the event.
std::deque< IJoystickListener * > m_joystickListeners
The Joystick listeners.
virtual void setActive(bool active)
Changes the listener status.
Definition: ilistener.h:49
EventSourceType getEventSourceType()
Gets the source type of this event.