2c11c96bc870e3458bbdadd1ec0af3558293a59c
[supertux.git] / src / control / joystick_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2013 Ingo Ruhnke <grumbel@gmx.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "control/joystick_manager.hpp"
18
19 #include <iostream>
20 #include <algorithm>
21
22 #include "control/joystickkeyboardcontroller.hpp"
23 #include "lisp/list_iterator.hpp"
24 #include "supertux/menu/joystick_menu.hpp"
25 #include "supertux/menu/menu_storage.hpp"
26 #include "util/gettext.hpp"
27 #include "util/log.hpp"
28 #include "util/writer.hpp"
29
30 JoystickManager::JoystickManager(JoystickKeyboardController* parent) :
31   parent(parent),
32   m_use_game_controller(true),
33   joy_button_map(),
34   joy_axis_map(),
35   joy_hat_map(),
36   dead_zone(4000),
37   min_joybuttons(),
38   max_joybuttons(),
39   max_joyaxis(),
40   max_joyhats(),
41   hat_state(0),
42   jump_with_up_joy(false),
43   wait_for_joystick(-1),
44   joysticks(),
45   game_controllers()
46 {
47   if (!m_use_game_controller)
48   {
49     // Default joystick button configuration
50     bind_joybutton(0, 0, Controller::JUMP);
51     bind_joybutton(0, 1, Controller::ACTION);
52     // 6 or more Buttons
53     if( min_joybuttons > 5 ){
54       bind_joybutton(0, 4, Controller::PEEK_LEFT);
55       bind_joybutton(0, 5, Controller::PEEK_RIGHT);
56       // 8 or more
57       if(min_joybuttons > 7)
58         bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
59     } else {
60       // map the last 2 buttons to menu and pause
61       if(min_joybuttons > 2)
62         bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
63       // map all remaining joystick buttons to MENU_SELECT
64       for(int i = 2; i < max_joybuttons; ++i) {
65         if(i != min_joybuttons-1)
66           bind_joybutton(0, i, Controller::MENU_SELECT);
67       }
68     }
69
70     // Default joystick axis configuration
71     bind_joyaxis(0, -1, Controller::LEFT);
72     bind_joyaxis(0, 1, Controller::RIGHT);
73     bind_joyaxis(0, -2, Controller::UP);
74     bind_joyaxis(0, 2, Controller::DOWN);
75   }
76 }
77
78 JoystickManager::~JoystickManager()
79 {
80   for(auto joy : joysticks)
81   {
82     SDL_JoystickClose(joy);
83   }
84
85   for(auto con : game_controllers)
86   {
87     SDL_GameControllerClose(con);
88   }
89 }
90
91 void
92 JoystickManager::on_joystick_added(int joystick_index)
93 {
94   std::cout << "joydeviceadded: " << joystick_index << std::endl;
95   if (m_use_game_controller)
96   {
97     if (!SDL_IsGameController(joystick_index))
98     {
99       log_warning << "joystick is not a game controller, ignoring: " << joystick_index << std::endl;
100     }
101     else
102     {
103       SDL_GameController* game_controller = SDL_GameControllerOpen(joystick_index);
104       if (!game_controller)
105       {
106         log_warning << "failed to open game_controller: " << joystick_index 
107                     << ": " << SDL_GetError() << std::endl;
108       }
109       else
110       {
111         game_controllers.push_back(game_controller);
112       }
113     }
114   }
115   else
116   {
117     SDL_Joystick* joystick = SDL_JoystickOpen(joystick_index);
118     if (!joystick)
119     {
120       log_warning << "failed to open joystick: " << joystick_index 
121                   << ": " << SDL_GetError() << std::endl;
122     }
123     else
124     {
125       joysticks.push_back(joystick);
126     }
127
128     if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
129       min_joybuttons = SDL_JoystickNumButtons(joystick);
130
131     if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
132       max_joybuttons = SDL_JoystickNumButtons(joystick);
133
134     if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
135       max_joyaxis = SDL_JoystickNumAxes(joystick);
136
137     if(SDL_JoystickNumHats(joystick) > max_joyhats)
138       max_joyhats = SDL_JoystickNumHats(joystick);
139   }
140 }
141
142 void
143 JoystickManager::on_joystick_removed(int instance_id)
144 {
145   if (m_use_game_controller)
146   {
147     for(auto& controller : game_controllers)
148     {
149       SDL_Joystick* joy = SDL_GameControllerGetJoystick(controller);
150       SDL_JoystickID id = SDL_JoystickInstanceID(joy);
151       if (id == instance_id)
152       {
153         SDL_GameControllerClose(controller);
154         controller = nullptr;
155       }
156     }
157
158     game_controllers.erase(std::remove(game_controllers.begin(), game_controllers.end(), nullptr),
159                            game_controllers.end());
160   }
161   else
162   {
163     std::cout << "joydeviceremoved: " << static_cast<int>(instance_id) << std::endl;
164     for(auto& joy : joysticks)
165     {
166       SDL_JoystickID id = SDL_JoystickInstanceID(joy);
167       if (id == instance_id)
168       {
169         SDL_JoystickClose(joy);
170         joy = nullptr;
171       }
172     }
173
174     joysticks.erase(std::remove(joysticks.begin(), joysticks.end(), nullptr),
175                     joysticks.end());
176   }
177 }
178
179 void
180 JoystickManager::process_hat_event(const SDL_JoyHatEvent& jhat)
181 {
182   Uint8 changed = hat_state ^ jhat.value;
183
184   if (wait_for_joystick >= 0)
185   {
186     if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
187       bind_joyhat(jhat.which, SDL_HAT_UP, Controller::Control(wait_for_joystick));
188
189     if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
190       bind_joyhat(jhat.which, SDL_HAT_DOWN, Controller::Control(wait_for_joystick));
191
192     if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
193       bind_joyhat(jhat.which, SDL_HAT_LEFT, Controller::Control(wait_for_joystick));
194
195     if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
196       bind_joyhat(jhat.which, SDL_HAT_RIGHT, Controller::Control(wait_for_joystick));
197
198     MenuStorage::get_joystick_options_menu()->update();
199     wait_for_joystick = -1;
200   }
201   else
202   {
203     if (changed & SDL_HAT_UP)
204     {
205       HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_UP));
206       if (it != joy_hat_map.end())
207         set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
208     }
209
210     if (changed & SDL_HAT_DOWN)
211     {
212       HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_DOWN));
213       if (it != joy_hat_map.end())
214         set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
215     }
216
217     if (changed & SDL_HAT_LEFT)
218     {
219       HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_LEFT));
220       if (it != joy_hat_map.end())
221         set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
222     }
223
224     if (changed & SDL_HAT_RIGHT)
225     {
226       HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_RIGHT));
227       if (it != joy_hat_map.end())
228         set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
229     }
230   }
231
232   hat_state = jhat.value;
233 }
234
235 void
236 JoystickManager::process_axis_event(const SDL_JoyAxisEvent& jaxis)
237 {
238   if (wait_for_joystick >= 0)
239   {
240     if (abs(jaxis.value) > dead_zone) {
241       if (jaxis.value < 0)
242         bind_joyaxis(jaxis.which, -(jaxis.axis + 1), Controller::Control(wait_for_joystick));
243       else
244         bind_joyaxis(jaxis.which, jaxis.axis + 1, Controller::Control(wait_for_joystick));
245
246       MenuStorage::get_joystick_options_menu()->update();
247       wait_for_joystick = -1;
248     }
249   }
250   else
251   {
252     // Split the axis into left and right, so that both can be
253     // mapped separately (needed for jump/down vs up/down)
254     int axis = jaxis.axis + 1;
255
256     AxisMap::iterator left  = joy_axis_map.find(std::make_pair(jaxis.which, -axis));
257     AxisMap::iterator right = joy_axis_map.find(std::make_pair(jaxis.which, axis));
258
259     if(left == joy_axis_map.end()) {
260       // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
261     } else {
262       if (jaxis.value < -dead_zone)
263         set_joy_controls(left->second,  true);
264       else
265         set_joy_controls(left->second, false);
266     }
267
268     if(right == joy_axis_map.end()) {
269       // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
270     } else {
271       if (jaxis.value > dead_zone)
272         set_joy_controls(right->second, true);
273       else
274         set_joy_controls(right->second, false);
275     }
276   }
277 }
278
279 void
280 JoystickManager::process_button_event(const SDL_JoyButtonEvent& jbutton)
281 {
282   if(wait_for_joystick >= 0) 
283   {
284     if(jbutton.state == SDL_PRESSED)
285     {
286       bind_joybutton(jbutton.which, jbutton.button, (Controller::Control)wait_for_joystick);
287       MenuStorage::get_joystick_options_menu()->update();
288       parent->reset();
289       wait_for_joystick = -1;
290     }
291   } 
292   else 
293   {
294     ButtonMap::iterator i = joy_button_map.find(std::make_pair(jbutton.which, jbutton.button));
295     if(i == joy_button_map.end()) {
296       log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
297     } else {
298       set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
299     }
300   }
301 }
302
303
304 int
305 JoystickManager::reversemap_joyaxis(Controller::Control c)
306 {
307   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
308     if(i->second == c)
309       return i->first.second;
310   }
311
312   return 0;
313 }
314
315 int
316 JoystickManager::reversemap_joybutton(Controller::Control c)
317 {
318   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
319     if(i->second == c)
320       return i->first.second;
321   }
322
323   return -1;
324 }
325
326 int
327 JoystickManager::reversemap_joyhat(Controller::Control c)
328 {
329   for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
330     if(i->second == c)
331       return i->first.second;
332   }
333
334   return -1;
335 }
336
337 void
338 JoystickManager::print_joystick_mappings()
339 {
340   std::cout << _("Joystick Mappings") << std::endl;
341   std::cout << "-----------------" << std::endl;
342   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
343     std::cout << "Axis: " << i->first.second << " -> " << i->second << std::endl;
344   }
345
346   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
347     std::cout << "Button: " << i->first.second << " -> " << i->second << std::endl;
348   }
349
350   for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
351     std::cout << "Hat: " << i->first.second << " -> " << i->second << std::endl;
352   }
353   std::cout << std::endl;
354 }
355
356 void
357 JoystickManager::unbind_joystick_control(Controller::Control control)
358 {
359   // remove all previous mappings for that control
360   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
361     if(i->second == control)
362       joy_axis_map.erase(i++);
363     else
364       ++i;
365   }
366
367   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
368     if(i->second == control)
369       joy_button_map.erase(i++);
370     else
371       ++i;
372   }
373
374   for(HatMap::iterator i = joy_hat_map.begin();  i != joy_hat_map.end(); /* no ++i */) {
375     if(i->second == control)
376       joy_hat_map.erase(i++);
377     else
378       ++i;
379   }
380 }
381
382 void
383 JoystickManager::bind_joyaxis(JoyId joy_id, int axis, Controller::Control control)
384 {
385   // axis isn't the SDL axis number, but axisnumber + 1 with sign
386   // changed depending on if the positive or negative end is to be
387   // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
388   // etc.)
389
390   unbind_joystick_control(control);
391
392   // add new mapping
393   joy_axis_map[std::make_pair(joy_id, axis)] = control;
394 }
395
396 void
397 JoystickManager::bind_joyhat(JoyId joy_id, int dir, Controller::Control c)
398 {
399   unbind_joystick_control(c);
400
401   // add new mapping
402   joy_hat_map[std::make_pair(joy_id, dir)] = c;
403 }
404
405 void
406 JoystickManager::bind_joybutton(JoyId joy_id, int button, Controller::Control control)
407 {
408   unbind_joystick_control(control);
409
410   // add new mapping
411   joy_button_map[std::make_pair(joy_id, button)] = control;
412 }
413
414 void
415 JoystickManager::read(const lisp::Lisp* joystick_lisp)
416 {
417   joystick_lisp->get("dead-zone", dead_zone);
418   joystick_lisp->get("jump-with-up", jump_with_up_joy);
419   lisp::ListIterator iter(joystick_lisp);
420   while(iter.next()) {
421     if(iter.item() == _("map")) {
422       int button = -1;
423       int axis   = 0;
424       int hat    = -1;
425       std::string control;
426       const lisp::Lisp* map = iter.lisp();
427
428       map->get("control", control);
429       int i = 0;
430       for(i = 0; Controller::controlNames[i] != 0; ++i) {
431         if(control == Controller::controlNames[i])
432           break;
433       }
434       if(Controller::controlNames[i] == 0) {
435         log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
436         continue;
437       }
438
439       bool js_available = joysticks.size() > 0;
440
441       if (map->get("button", button)) {
442         if(js_available && (button < 0 || button >= max_joybuttons)) {
443           log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
444           continue;
445         }
446         bind_joybutton(0, button, Controller::Control(i));
447       }
448
449       if (map->get("axis",   axis)) {
450         if (js_available && (axis == 0 || abs(axis) > max_joyaxis)) {
451           log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
452           continue;
453         }
454         bind_joyaxis(0, axis, Controller::Control(i));
455       }
456
457       if (map->get("hat",   hat)) {
458         if (js_available        &&
459             hat != SDL_HAT_UP   &&
460             hat != SDL_HAT_DOWN &&
461             hat != SDL_HAT_LEFT &&
462             hat != SDL_HAT_RIGHT) {
463           log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
464           continue;
465         } else {
466           bind_joyhat(0, hat, Controller::Control(i));
467         }
468       }
469     }
470   }
471 }
472
473 void
474 JoystickManager::write(Writer& writer)
475 {
476   writer.write("dead-zone", dead_zone);
477   writer.write("jump-with-up", jump_with_up_joy);
478
479   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
480       ++i) {
481     writer.start_list("map");
482     writer.write("button", i->first.second);
483     writer.write("control", Controller::controlNames[i->second]);
484     writer.end_list("map");
485   }
486
487   for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
488     writer.start_list("map");
489     writer.write("hat", i->first.second);
490     writer.write("control", Controller::controlNames[i->second]);
491     writer.end_list("map");
492   }
493
494   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
495     writer.start_list("map");
496     writer.write("axis", i->first.second);
497     writer.write("control", Controller::controlNames[i->second]);
498     writer.end_list("map");
499   }
500 }
501
502 void
503 JoystickManager::set_joy_controls(Controller::Control id, bool value)
504 {
505   if (jump_with_up_joy && id == Controller::UP)
506   {
507     parent->get_main_controller()->set_control(Controller::JUMP, value);
508   }
509
510   parent->get_main_controller()->set_control(id, value);
511 }
512
513 /* EOF */