ManaPlus
joystick.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "input/joystick.h"
25 
26 #include "configuration.h"
27 #include "logger.h"
28 #include "settings.h"
29 #include "sdlshared.h"
30 
31 #include "input/inputmanager.h"
32 
33 #include "utils/foreach.h"
34 #include "utils/timer.h"
35 
36 PRAGMA48(GCC diagnostic push)
37 PRAGMA48(GCC diagnostic ignored "-Wshadow")
38 #include <SDL.h>
39 PRAGMA48(GCC diagnostic pop)
40 
41 #include "debug.h"
42 
43 Joystick *joystick = nullptr;
45 bool Joystick::mEnabled = false;
46 
47 Joystick::Joystick(const int no) :
48  mDirection(0),
49  mJoystick(nullptr),
50  mUpTolerance(0),
51  mDownTolerance(0),
52  mLeftTolerance(0),
53  mRightTolerance(0),
54  mCalibrating(false),
55  mNumber(no >= joystickCount ? joystickCount : no),
56  mCalibrated(false),
57  mButtonsNumber(MAX_BUTTONS),
58  mUseInactive(false),
59  mHaveHats(false),
60  mKeyToAction(),
61  mKeyToId(),
62  mKeyTimeMap()
63 {
64  for (int i = 0; i < MAX_BUTTONS; i++)
65  mActiveButtons[i] = false;
66 }
67 
69 {
70  close();
71 }
72 
74 {
75  SDL_InitSubSystem(SDL_INIT_JOYSTICK);
76  // +++ possible to use SDL_EventState with different joystick features.
77  SDL_JoystickEventState(SDL_ENABLE);
78  joystickCount = SDL_NumJoysticks();
79  logger->log("%i joysticks/gamepads found", joystickCount);
80  for (int i = 0; i < joystickCount; i++)
81  logger->log("- %s", SDL_JoystickNameForIndex(i));
82 
83  mEnabled = config.getBoolValue("joystickEnabled");
84 
85  if (joystickCount > 0)
86  {
87  joystick = new Joystick(config.getIntValue("selectedJoystick"));
88  if (mEnabled)
89  joystick->open();
90  }
91 }
92 
94 {
95  if (mNumber >= joystickCount)
96  mNumber = joystickCount - 1;
97  if (mNumber < 0)
98  {
99  logger->log1("error: incorrect joystick selection");
100  return false;
101  }
102  logger->log("open joystick %d", mNumber);
103 
104  mJoystick = SDL_JoystickOpen(mNumber);
105 
106  if (mJoystick == nullptr)
107  {
108  logger->log("Couldn't open joystick: %s", SDL_GetError());
109  return false;
110  }
111 
112  mButtonsNumber = SDL_JoystickNumButtons(mJoystick);
113  logger->log("Joystick: %i ", mNumber);
114 #ifdef USE_SDL2
115  logger->log("Name: %s", SDL_JoystickName(mJoystick));
116  SDL_JoystickGUID guid = SDL_JoystickGetGUID(mJoystick);
117  std::string guidStr;
118  for (int f = 0; f < 16; f ++)
119  guidStr.append(strprintf("%02x", CAST_U32(guid.data[f])));
120  logger->log("Guid: %s", guidStr.c_str());
121 #if SDL_VERSION_ATLEAST(2, 0, 6)
122  logger->log("Device id: %u:%u.%u",
123  CAST_U32(SDL_JoystickGetVendor(mJoystick)),
124  CAST_U32(SDL_JoystickGetProduct(mJoystick)),
125  CAST_U32(SDL_JoystickGetProductVersion(mJoystick)));
126 
127  SDL_JoystickType type = SDL_JoystickGetType(mJoystick);
128  std::string typeStr;
129  switch (type)
130  {
131  default:
132  case SDL_JOYSTICK_TYPE_UNKNOWN:
133  typeStr = "unknown";
134  break;
135  case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
136  typeStr = "game controller";
137  break;
138  case SDL_JOYSTICK_TYPE_WHEEL:
139  typeStr = "wheel";
140  break;
141  case SDL_JOYSTICK_TYPE_ARCADE_STICK:
142  typeStr = "arcade stick";
143  break;
144  case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
145  typeStr = "flight stick";
146  break;
147  case SDL_JOYSTICK_TYPE_DANCE_PAD:
148  typeStr = "dance pad";
149  break;
150  case SDL_JOYSTICK_TYPE_GUITAR:
151  typeStr = "guitar";
152  break;
153  case SDL_JOYSTICK_TYPE_DRUM_KIT:
154  typeStr = "drum kit";
155  break;
156  case SDL_JOYSTICK_TYPE_ARCADE_PAD:
157  typeStr = "arcade pad";
158  break;
159  case SDL_JOYSTICK_TYPE_THROTTLE:
160  typeStr = "throttle";
161  break;
162  }
163  logger->log("Type: " + typeStr);
164 #endif // SDL_VERSION_ATLEAST(2, 0, 6)
165  // probably need aslo dump SDL_JoystickCurrentPowerLevel
166 #else // USE_SDL2
167 
168  logger->log("Name: %s", SDL_JoystickName(mNumber));
169 #endif // USE_SDL2
170 
171  logger->log("Axes: %i ", SDL_JoystickNumAxes(mJoystick));
172  logger->log("Balls: %i", SDL_JoystickNumBalls(mJoystick));
173  logger->log("Hats: %i", SDL_JoystickNumHats(mJoystick));
174  logger->log("Buttons: %i", mButtonsNumber);
175 
176  mHaveHats = (SDL_JoystickNumHats(mJoystick) > 0);
177 
180 
181 #ifdef __SWITCH__
182  config.setValue("joystick" + toString(mNumber) + "calibrated", true);
183  config.setValue("leftTolerance" + toString(mNumber), -10000);
184  config.setValue("rightTolerance" + toString(mNumber), 10000);
185  config.setValue("upTolerance" + toString(mNumber), -10000);
186  config.setValue("downTolerance" + toString(mNumber), 10000);
187 #endif
188  mCalibrated = config.getValueBool("joystick"
189  + toString(mNumber) + "calibrated", false);
190 
191  mUpTolerance = config.getIntValue("upTolerance" + toString(mNumber));
192  mDownTolerance = config.getIntValue("downTolerance" + toString(mNumber));
193  mLeftTolerance = config.getIntValue("leftTolerance" + toString(mNumber));
194  mRightTolerance = config.getIntValue("rightTolerance" + toString(mNumber));
195  mUseInactive = config.getBoolValue("useInactiveJoystick");
196 
197  return true;
198 }
199 
201 {
202  logger->log("close joystick %d", mNumber);
203  if (mJoystick != nullptr)
204  {
205  SDL_JoystickClose(mJoystick);
206  mJoystick = nullptr;
207  }
208 }
209 
211 {
212  joystickCount = SDL_NumJoysticks();
214 }
215 
216 void Joystick::setNumber(const int n)
217 {
218  if (mJoystick != nullptr)
219  {
220  SDL_JoystickClose(mJoystick);
221  mNumber = n;
222  open();
223  }
224  else
225  {
226  mNumber = n;
227  }
228 }
229 
231 {
232  BLOCK_START("Joystick::logic")
233  // When calibrating, don't bother the outside with our state
234  if (mCalibrating)
235  {
236  doCalibration();
237  BLOCK_END("Joystick::logic")
238  return;
239  }
240 
241  if (!mEnabled || !mCalibrated)
242  {
243  BLOCK_END("Joystick::logic")
244  return;
245  }
246 
247  mDirection = 0;
248 
249  if (mUseInactive ||
251  {
252  // X-Axis
253  int position = SDL_JoystickGetAxis(mJoystick, 0);
254  if (position >= mRightTolerance)
255  mDirection |= RIGHT;
256  else if (position <= mLeftTolerance)
257  mDirection |= LEFT;
258 
259  // Y-Axis
260  position = SDL_JoystickGetAxis(mJoystick, 1);
261  if (position <= mUpTolerance)
262  mDirection |= UP;
263  else if (position >= mDownTolerance)
264  mDirection |= DOWN;
265 
266 #ifdef DEBUG_JOYSTICK
267  if (SDL_JoystickGetAxis(mJoystick, 2))
268  logger->log("axis 2 pos: %d", SDL_JoystickGetAxis(mJoystick, 2));
269  if (SDL_JoystickGetAxis(mJoystick, 3))
270  logger->log("axis 3 pos: %d", SDL_JoystickGetAxis(mJoystick, 3));
271  if (SDL_JoystickGetAxis(mJoystick, 4))
272  logger->log("axis 4 pos: %d", SDL_JoystickGetAxis(mJoystick, 4));
273 #endif // DEBUG_JOYSTICK
274 
275  if ((mDirection == 0U) && mHaveHats)
276  {
277  // reading only hat 0
278  const uint8_t hat = SDL_JoystickGetHat(mJoystick, 0);
279  if ((hat & SDL_HAT_RIGHT) != 0)
280  mDirection |= RIGHT;
281  else if ((hat & SDL_HAT_LEFT) != 0)
282  mDirection |= LEFT;
283  if ((hat & SDL_HAT_UP) != 0)
284  mDirection |= UP;
285  else if ((hat & SDL_HAT_DOWN) != 0)
286  mDirection |= DOWN;
287  }
288 
289  // Buttons
290  for (int i = 0; i < mButtonsNumber; i++)
291  {
292  const bool state = (SDL_JoystickGetButton(mJoystick, i) == 1);
293  mActiveButtons[i] = state;
294  if (!state)
295  resetRepeat(i);
296 #ifdef DEBUG_JOYSTICK
297  if (mActiveButtons[i])
298  logger->log("button: %d", i);
299 #endif // DEBUG_JOYSTICK
300  }
301  }
302  else
303  {
304  for (int i = 0; i < mButtonsNumber; i++)
305  mActiveButtons[i] = false;
306  }
307  BLOCK_END("Joystick::logic")
308 }
309 
311 {
312  mUpTolerance = 0;
313  mDownTolerance = 0;
314  mLeftTolerance = 0;
315  mRightTolerance = 0;
316  mCalibrating = true;
317 }
318 
320 {
321  // X-Axis
322  int position = SDL_JoystickGetAxis(mJoystick, 0);
323  if (position > mRightTolerance)
324  mRightTolerance = position;
325  else if (position < mLeftTolerance)
326  mLeftTolerance = position;
327 
328  // Y-Axis
329  position = SDL_JoystickGetAxis(mJoystick, 1);
330  if (position > mDownTolerance)
331  mDownTolerance = position;
332  else if (position < mUpTolerance)
333  mUpTolerance = position;
334 }
335 
337 {
338  mCalibrated = true;
339  mCalibrating = false;
340  config.setValue("joystick" + toString(mNumber) + "calibrated", true);
341  config.setValue("leftTolerance" + toString(mNumber), mLeftTolerance);
342  config.setValue("rightTolerance" + toString(mNumber), mRightTolerance);
343  config.setValue("upTolerance" + toString(mNumber), mUpTolerance);
344  config.setValue("downTolerance" + toString(mNumber), mDownTolerance);
345 }
346 
347 bool Joystick::buttonPressed(const unsigned char no) const
348 {
349  return (mEnabled && no < MAX_BUTTONS) ? mActiveButtons[no] : false;
350 }
351 
352 void Joystick::getNames(STD_VECTOR <std::string> &names)
353 {
354  names.clear();
355  for (int i = 0; i < joystickCount; i++)
356  names.push_back(SDL_JoystickNameForIndex(i));
357 }
358 
360 {
363 }
364 
365 KeysVector *Joystick::getActionVector(const SDL_Event &event)
366 {
367  const int i = getButtonFromEvent(event);
368 
369  if (i < 0 || i >= mButtonsNumber)
370  return nullptr;
371 // logger->log("button triggerAction: %d", i);
372  if (mKeyToAction.find(i) != mKeyToAction.end())
373  return &mKeyToAction[i];
374  return nullptr;
375 }
376 
378 {
379  if (i < 0 || i >= mButtonsNumber)
380  return nullptr;
381 // logger->log("button triggerAction: %d", i);
382  if (mKeyToAction.find(i) != mKeyToAction.end())
383  return &mKeyToAction[i];
384  return nullptr;
385 }
386 
387 int Joystick::getButtonFromEvent(const SDL_Event &event) const
388 {
389  if (event.jbutton.which != mNumber)
390  return -1;
391  return event.jbutton.button;
392 }
393 
394 bool Joystick::isActionActive(const InputActionT index) const
395 {
396  if (!validate())
397  return false;
398 
399  const InputFunction &key = inputManager.getKey(index);
400  for (size_t i = 0; i < inputFunctionSize; i ++)
401  {
402  const InputItem &val = key.values[i];
403  if (val.type != InputType::JOYSTICK)
404  continue;
405  const int value = val.value;
406  if (value >= 0 && value < mButtonsNumber)
407  {
408  if (mActiveButtons[value])
409  return true;
410  }
411  }
412  return false;
413 }
414 
415 bool Joystick::validate() const
416 {
417  if (mCalibrating || !mEnabled || !mCalibrated)
418  return false;
419 
420  return mUseInactive ||
422 }
423 
424 void Joystick::handleRepeat(const int time)
425 {
426  BLOCK_START("Joystick::handleRepeat")
428  {
429  bool repeat(false);
430  const int key = (*it).first;
431  if (key >= 0 && key < mButtonsNumber)
432  {
433  if (mActiveButtons[key])
434  repeat = true;
435  }
436  if (repeat)
437  {
438  int &keyTime = (*it).second;
439  if (time > keyTime && abs(time - keyTime)
440  > SDL_DEFAULT_REPEAT_DELAY * 10)
441  {
442  keyTime = time;
444  }
445  }
446  }
447  BLOCK_END("Joystick::handleRepeat")
448 }
449 
450 void Joystick::resetRepeat(const int key)
451 {
452  const KeyTimeMapIter it = mKeyTimeMap.find(key);
453  if (it != mKeyTimeMap.end())
454  (*it).second = tick_time;
455 }
#define CAST_U32
Definition: cast.h:31
bool getValueBool(const std::string &key, const bool deflt) const
bool getBoolValue(const std::string &key) const
void setValue(const std::string &key, const std::string &value)
int getIntValue(const std::string &key) const
void updateKeyActionMap(KeyToActionMap &actionMap, KeyToIdMap &idMap, KeyTimeMap &keyTimeMap, const InputTypeT type) const
InputFunction & getKey(InputActionT index) A_CONST
bool triggerAction(const KeysVector *const ptrs)
SDL_Joystick * mJoystick
Definition: joystick.h:146
KeysVector * getActionVector(const SDL_Event &event)
Definition: joystick.cpp:365
void reload()
Definition: joystick.cpp:210
static bool mEnabled
Definition: joystick.h:168
void finishCalibration()
Definition: joystick.cpp:336
static void init()
Definition: joystick.cpp:73
@ MAX_BUTTONS
Definition: joystick.h:42
void handleRepeat(const int time)
Definition: joystick.cpp:424
int mUpTolerance
Definition: joystick.h:148
int mLeftTolerance
Definition: joystick.h:150
~Joystick()
Definition: joystick.cpp:68
static int joystickCount
Definition: joystick.h:169
void setNumber(const int n)
Definition: joystick.cpp:216
void startCalibration()
Definition: joystick.cpp:310
void update()
Definition: joystick.cpp:359
int mButtonsNumber
Definition: joystick.h:155
void logic()
Definition: joystick.cpp:230
KeysVector * getActionVectorByKey(const int i)
Definition: joystick.cpp:377
bool mHaveHats
Definition: joystick.h:157
static void getNames(std::vector< std::string > &names)
Definition: joystick.cpp:352
bool open()
Definition: joystick.cpp:93
@ DOWN
Definition: joystick.h:51
@ RIGHT
Definition: joystick.h:53
@ LEFT
Definition: joystick.h:52
Joystick(const int no)
Definition: joystick.cpp:47
bool mCalibrated
Definition: joystick.h:154
bool mUseInactive
Definition: joystick.h:156
void close()
Definition: joystick.cpp:200
void doCalibration()
Definition: joystick.cpp:319
void resetRepeat(const int key)
Definition: joystick.cpp:450
bool mActiveButtons[MAX_BUTTONS]
Definition: joystick.h:144
KeyToIdMap mKeyToId
Definition: joystick.h:161
bool isActionActive(const InputActionT index) const
Definition: joystick.cpp:394
bool buttonPressed(const unsigned char no) const
Definition: joystick.cpp:347
int getButtonFromEvent(const SDL_Event &event) const
Definition: joystick.cpp:387
unsigned char mDirection
Definition: joystick.h:142
bool validate() const
Definition: joystick.cpp:415
int mRightTolerance
Definition: joystick.h:151
KeyToActionMap mKeyToAction
Definition: joystick.h:159
int mNumber
Definition: joystick.h:153
bool mCalibrating
Definition: joystick.h:152
KeyTimeMap mKeyTimeMap
Definition: joystick.h:163
int mDownTolerance
Definition: joystick.h:149
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log1(const char *const log_text)
Definition: logger.cpp:238
KeyboardFocusT inputFocused
Definition: settings.h:155
Configuration config
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
InputAction ::T InputActionT
Definition: inputaction.h:717
KeyTimeMap::iterator KeyTimeMapIter
Definition: inputevent.h:47
std::vector< InputActionT > KeysVector
Definition: inputevent.h:34
const size_t inputFunctionSize
Definition: inputfunction.h:29
InputManager inputManager
Joystick * joystick
Definition: joystick.cpp:43
#define PRAGMA48(str)
Definition: localconsts.h:199
#define nullptr
Definition: localconsts.h:45
Logger * logger
Definition: logger.cpp:89
volatile int tick_time
Definition: timer.cpp:53
bool hat(InputEvent &event)
Definition: chat.cpp:62
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
@ JOYSTICK
Definition: inputtype.h:40
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
#define SDL_JoystickNameForIndex
Definition: sdlshared.h:61
Settings settings
Definition: settings.cpp:32
std::string strprintf(const char *const format,...)
InputItem values[inputFunctionSize]
Definition: inputfunction.h:35
int value
Definition: inputitem.h:45
InputTypeT type
Definition: inputitem.h:44