2 // Copyright (C) 2013 Ingo Ruhnke <grumbel@gmx.de>
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.
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.
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/>.
17 #include "control/joystick_manager.hpp"
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"
30 JoystickManager::JoystickManager(JoystickKeyboardController* parent) :
32 m_use_game_controller(true),
42 jump_with_up_joy(false),
43 wait_for_joystick(-1),
47 if (!m_use_game_controller)
49 // Default joystick button configuration
50 bind_joybutton(0, 0, Controller::JUMP);
51 bind_joybutton(0, 1, Controller::ACTION);
53 if( min_joybuttons > 5 ){
54 bind_joybutton(0, 4, Controller::PEEK_LEFT);
55 bind_joybutton(0, 5, Controller::PEEK_RIGHT);
57 if(min_joybuttons > 7)
58 bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
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);
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);
78 JoystickManager::~JoystickManager()
80 for(auto joy : joysticks)
82 SDL_JoystickClose(joy);
85 for(auto con : game_controllers)
87 SDL_GameControllerClose(con);
92 JoystickManager::on_joystick_added(int joystick_index)
94 std::cout << "joydeviceadded: " << joystick_index << std::endl;
95 if (m_use_game_controller)
97 if (!SDL_IsGameController(joystick_index))
99 log_warning << "joystick is not a game controller, ignoring: " << joystick_index << std::endl;
103 SDL_GameController* game_controller = SDL_GameControllerOpen(joystick_index);
104 if (!game_controller)
106 log_warning << "failed to open game_controller: " << joystick_index
107 << ": " << SDL_GetError() << std::endl;
111 game_controllers.push_back(game_controller);
117 SDL_Joystick* joystick = SDL_JoystickOpen(joystick_index);
120 log_warning << "failed to open joystick: " << joystick_index
121 << ": " << SDL_GetError() << std::endl;
125 joysticks.push_back(joystick);
128 if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
129 min_joybuttons = SDL_JoystickNumButtons(joystick);
131 if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
132 max_joybuttons = SDL_JoystickNumButtons(joystick);
134 if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
135 max_joyaxis = SDL_JoystickNumAxes(joystick);
137 if(SDL_JoystickNumHats(joystick) > max_joyhats)
138 max_joyhats = SDL_JoystickNumHats(joystick);
143 JoystickManager::on_joystick_removed(int instance_id)
145 if (m_use_game_controller)
147 for(auto& controller : game_controllers)
149 SDL_Joystick* joy = SDL_GameControllerGetJoystick(controller);
150 SDL_JoystickID id = SDL_JoystickInstanceID(joy);
151 if (id == instance_id)
153 SDL_GameControllerClose(controller);
154 controller = nullptr;
158 game_controllers.erase(std::remove(game_controllers.begin(), game_controllers.end(), nullptr),
159 game_controllers.end());
163 std::cout << "joydeviceremoved: " << static_cast<int>(instance_id) << std::endl;
164 for(auto& joy : joysticks)
166 SDL_JoystickID id = SDL_JoystickInstanceID(joy);
167 if (id == instance_id)
169 SDL_JoystickClose(joy);
174 joysticks.erase(std::remove(joysticks.begin(), joysticks.end(), nullptr),
180 JoystickManager::process_hat_event(const SDL_JoyHatEvent& jhat)
182 Uint8 changed = hat_state ^ jhat.value;
184 if (wait_for_joystick >= 0)
186 if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
187 bind_joyhat(jhat.which, SDL_HAT_UP, Controller::Control(wait_for_joystick));
189 if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
190 bind_joyhat(jhat.which, SDL_HAT_DOWN, Controller::Control(wait_for_joystick));
192 if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
193 bind_joyhat(jhat.which, SDL_HAT_LEFT, Controller::Control(wait_for_joystick));
195 if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
196 bind_joyhat(jhat.which, SDL_HAT_RIGHT, Controller::Control(wait_for_joystick));
198 MenuStorage::get_joystick_options_menu()->update();
199 wait_for_joystick = -1;
203 if (changed & SDL_HAT_UP)
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);
210 if (changed & SDL_HAT_DOWN)
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);
217 if (changed & SDL_HAT_LEFT)
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);
224 if (changed & SDL_HAT_RIGHT)
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);
232 hat_state = jhat.value;
236 JoystickManager::process_axis_event(const SDL_JoyAxisEvent& jaxis)
238 if (wait_for_joystick >= 0)
240 if (abs(jaxis.value) > dead_zone) {
242 bind_joyaxis(jaxis.which, -(jaxis.axis + 1), Controller::Control(wait_for_joystick));
244 bind_joyaxis(jaxis.which, jaxis.axis + 1, Controller::Control(wait_for_joystick));
246 MenuStorage::get_joystick_options_menu()->update();
247 wait_for_joystick = -1;
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;
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));
259 if(left == joy_axis_map.end()) {
260 // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
262 if (jaxis.value < -dead_zone)
263 set_joy_controls(left->second, true);
265 set_joy_controls(left->second, false);
268 if(right == joy_axis_map.end()) {
269 // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
271 if (jaxis.value > dead_zone)
272 set_joy_controls(right->second, true);
274 set_joy_controls(right->second, false);
280 JoystickManager::process_button_event(const SDL_JoyButtonEvent& jbutton)
282 if(wait_for_joystick >= 0)
284 if(jbutton.state == SDL_PRESSED)
286 bind_joybutton(jbutton.which, jbutton.button, (Controller::Control)wait_for_joystick);
287 MenuStorage::get_joystick_options_menu()->update();
289 wait_for_joystick = -1;
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;
298 set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
305 JoystickManager::reversemap_joyaxis(Controller::Control c)
307 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
309 return i->first.second;
316 JoystickManager::reversemap_joybutton(Controller::Control c)
318 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
320 return i->first.second;
327 JoystickManager::reversemap_joyhat(Controller::Control c)
329 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
331 return i->first.second;
338 JoystickManager::print_joystick_mappings()
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;
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;
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;
353 std::cout << std::endl;
357 JoystickManager::unbind_joystick_control(Controller::Control control)
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++);
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++);
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++);
383 JoystickManager::bind_joyaxis(JoyId joy_id, int axis, Controller::Control control)
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,
390 unbind_joystick_control(control);
393 joy_axis_map[std::make_pair(joy_id, axis)] = control;
397 JoystickManager::bind_joyhat(JoyId joy_id, int dir, Controller::Control c)
399 unbind_joystick_control(c);
402 joy_hat_map[std::make_pair(joy_id, dir)] = c;
406 JoystickManager::bind_joybutton(JoyId joy_id, int button, Controller::Control control)
408 unbind_joystick_control(control);
411 joy_button_map[std::make_pair(joy_id, button)] = control;
415 JoystickManager::read(const lisp::Lisp* joystick_lisp)
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);
421 if(iter.item() == _("map")) {
426 const lisp::Lisp* map = iter.lisp();
428 map->get("control", control);
430 for(i = 0; Controller::controlNames[i] != 0; ++i) {
431 if(control == Controller::controlNames[i])
434 if(Controller::controlNames[i] == 0) {
435 log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
439 bool js_available = joysticks.size() > 0;
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;
446 bind_joybutton(0, button, Controller::Control(i));
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;
454 bind_joyaxis(0, axis, Controller::Control(i));
457 if (map->get("hat", hat)) {
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;
466 bind_joyhat(0, hat, Controller::Control(i));
474 JoystickManager::write(Writer& writer)
476 writer.write("dead-zone", dead_zone);
477 writer.write("jump-with-up", jump_with_up_joy);
479 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
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");
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");
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");
503 JoystickManager::set_joy_controls(Controller::Control id, bool value)
505 if (jump_with_up_joy && id == Controller::UP)
507 parent->get_main_controller()->set_control(Controller::JUMP, value);
510 parent->get_main_controller()->set_control(id, value);