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 "gui/menu_manager.hpp"
23 #include "control/input_manager.hpp"
24 #include "lisp/list_iterator.hpp"
25 #include "supertux/menu/joystick_menu.hpp"
26 #include "util/gettext.hpp"
27 #include "util/log.hpp"
28 #include "util/writer.hpp"
30 JoystickManager::JoystickManager(InputManager* parent) :
41 jump_with_up_joy(false),
42 wait_for_joystick(-1),
45 // Default joystick button configuration
46 bind_joybutton(0, 0, Controller::JUMP);
47 bind_joybutton(0, 1, Controller::ACTION);
49 if( min_joybuttons > 5 ){
50 bind_joybutton(0, 4, Controller::PEEK_LEFT);
51 bind_joybutton(0, 5, Controller::PEEK_RIGHT);
53 if(min_joybuttons > 7)
54 bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
56 // map the last 2 buttons to menu and pause
57 if(min_joybuttons > 2)
58 bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
59 // map all remaining joystick buttons to MENU_SELECT
60 for(int i = 2; i < max_joybuttons; ++i) {
61 if(i != min_joybuttons-1)
62 bind_joybutton(0, i, Controller::MENU_SELECT);
66 // Default joystick axis configuration
67 bind_joyaxis(0, -1, Controller::LEFT);
68 bind_joyaxis(0, 1, Controller::RIGHT);
69 bind_joyaxis(0, -2, Controller::UP);
70 bind_joyaxis(0, 2, Controller::DOWN);
73 JoystickManager::~JoystickManager()
75 for(auto joy : joysticks)
77 SDL_JoystickClose(joy);
82 JoystickManager::on_joystick_added(int joystick_index)
84 std::cout << "joydeviceadded: " << joystick_index << std::endl;
85 SDL_Joystick* joystick = SDL_JoystickOpen(joystick_index);
88 log_warning << "failed to open joystick: " << joystick_index
89 << ": " << SDL_GetError() << std::endl;
93 joysticks.push_back(joystick);
96 if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
97 min_joybuttons = SDL_JoystickNumButtons(joystick);
99 if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
100 max_joybuttons = SDL_JoystickNumButtons(joystick);
102 if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
103 max_joyaxis = SDL_JoystickNumAxes(joystick);
105 if(SDL_JoystickNumHats(joystick) > max_joyhats)
106 max_joyhats = SDL_JoystickNumHats(joystick);
110 JoystickManager::on_joystick_removed(int instance_id)
112 std::cout << "joydeviceremoved: " << static_cast<int>(instance_id) << std::endl;
113 for(auto& joy : joysticks)
115 SDL_JoystickID id = SDL_JoystickInstanceID(joy);
116 if (id == instance_id)
118 SDL_JoystickClose(joy);
123 joysticks.erase(std::remove(joysticks.begin(), joysticks.end(), nullptr),
128 JoystickManager::process_hat_event(const SDL_JoyHatEvent& jhat)
130 Uint8 changed = hat_state ^ jhat.value;
132 if (wait_for_joystick >= 0)
134 if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
135 bind_joyhat(jhat.which, SDL_HAT_UP, Controller::Control(wait_for_joystick));
137 if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
138 bind_joyhat(jhat.which, SDL_HAT_DOWN, Controller::Control(wait_for_joystick));
140 if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
141 bind_joyhat(jhat.which, SDL_HAT_LEFT, Controller::Control(wait_for_joystick));
143 if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
144 bind_joyhat(jhat.which, SDL_HAT_RIGHT, Controller::Control(wait_for_joystick));
146 MenuManager::instance().refresh();
147 wait_for_joystick = -1;
151 if (changed & SDL_HAT_UP)
153 HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_UP));
154 if (it != joy_hat_map.end())
155 set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
158 if (changed & SDL_HAT_DOWN)
160 HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_DOWN));
161 if (it != joy_hat_map.end())
162 set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
165 if (changed & SDL_HAT_LEFT)
167 HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_LEFT));
168 if (it != joy_hat_map.end())
169 set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
172 if (changed & SDL_HAT_RIGHT)
174 HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_RIGHT));
175 if (it != joy_hat_map.end())
176 set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
180 hat_state = jhat.value;
184 JoystickManager::process_axis_event(const SDL_JoyAxisEvent& jaxis)
186 if (wait_for_joystick >= 0)
188 if (abs(jaxis.value) > dead_zone) {
190 bind_joyaxis(jaxis.which, -(jaxis.axis + 1), Controller::Control(wait_for_joystick));
192 bind_joyaxis(jaxis.which, jaxis.axis + 1, Controller::Control(wait_for_joystick));
194 MenuManager::instance().refresh();
195 wait_for_joystick = -1;
200 // Split the axis into left and right, so that both can be
201 // mapped separately (needed for jump/down vs up/down)
202 int axis = jaxis.axis + 1;
204 AxisMap::iterator left = joy_axis_map.find(std::make_pair(jaxis.which, -axis));
205 AxisMap::iterator right = joy_axis_map.find(std::make_pair(jaxis.which, axis));
207 if(left == joy_axis_map.end()) {
208 // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
210 if (jaxis.value < -dead_zone)
211 set_joy_controls(left->second, true);
213 set_joy_controls(left->second, false);
216 if(right == joy_axis_map.end()) {
217 // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
219 if (jaxis.value > dead_zone)
220 set_joy_controls(right->second, true);
222 set_joy_controls(right->second, false);
228 JoystickManager::process_button_event(const SDL_JoyButtonEvent& jbutton)
230 if(wait_for_joystick >= 0)
232 if(jbutton.state == SDL_PRESSED)
234 bind_joybutton(jbutton.which, jbutton.button, (Controller::Control)wait_for_joystick);
235 MenuManager::instance().refresh();
237 wait_for_joystick = -1;
242 ButtonMap::iterator i = joy_button_map.find(std::make_pair(jbutton.which, jbutton.button));
243 if(i == joy_button_map.end()) {
244 log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
246 set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
253 JoystickManager::reversemap_joyaxis(Controller::Control c)
255 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
257 return i->first.second;
264 JoystickManager::reversemap_joybutton(Controller::Control c)
266 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
268 return i->first.second;
275 JoystickManager::reversemap_joyhat(Controller::Control c)
277 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
279 return i->first.second;
286 JoystickManager::print_joystick_mappings()
288 std::cout << _("Joystick Mappings") << std::endl;
289 std::cout << "-----------------" << std::endl;
290 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
291 std::cout << "Axis: " << i->first.second << " -> " << i->second << std::endl;
294 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
295 std::cout << "Button: " << i->first.second << " -> " << i->second << std::endl;
298 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
299 std::cout << "Hat: " << i->first.second << " -> " << i->second << std::endl;
301 std::cout << std::endl;
305 JoystickManager::unbind_joystick_control(Controller::Control control)
307 // remove all previous mappings for that control
308 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
309 if(i->second == control)
310 joy_axis_map.erase(i++);
315 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
316 if(i->second == control)
317 joy_button_map.erase(i++);
322 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); /* no ++i */) {
323 if(i->second == control)
324 joy_hat_map.erase(i++);
331 JoystickManager::bind_joyaxis(JoyId joy_id, int axis, Controller::Control control)
333 // axis isn't the SDL axis number, but axisnumber + 1 with sign
334 // changed depending on if the positive or negative end is to be
335 // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
338 unbind_joystick_control(control);
341 joy_axis_map[std::make_pair(joy_id, axis)] = control;
345 JoystickManager::bind_joyhat(JoyId joy_id, int dir, Controller::Control c)
347 unbind_joystick_control(c);
350 joy_hat_map[std::make_pair(joy_id, dir)] = c;
354 JoystickManager::bind_joybutton(JoyId joy_id, int button, Controller::Control control)
356 unbind_joystick_control(control);
359 joy_button_map[std::make_pair(joy_id, button)] = control;
363 JoystickManager::read(const lisp::Lisp* joystick_lisp)
365 joystick_lisp->get("dead-zone", dead_zone);
366 joystick_lisp->get("jump-with-up", jump_with_up_joy);
367 lisp::ListIterator iter(joystick_lisp);
369 if(iter.item() == _("map")) {
374 const lisp::Lisp* map = iter.lisp();
376 map->get("control", control);
378 for(i = 0; Controller::controlNames[i] != 0; ++i) {
379 if(control == Controller::controlNames[i])
382 if(Controller::controlNames[i] == 0) {
383 log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
387 bool js_available = joysticks.size() > 0;
389 if (map->get("button", button)) {
390 if(js_available && (button < 0 || button >= max_joybuttons)) {
391 log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
394 bind_joybutton(0, button, Controller::Control(i));
397 if (map->get("axis", axis)) {
398 if (js_available && (axis == 0 || abs(axis) > max_joyaxis)) {
399 log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
402 bind_joyaxis(0, axis, Controller::Control(i));
405 if (map->get("hat", hat)) {
408 hat != SDL_HAT_DOWN &&
409 hat != SDL_HAT_LEFT &&
410 hat != SDL_HAT_RIGHT) {
411 log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
414 bind_joyhat(0, hat, Controller::Control(i));
422 JoystickManager::write(Writer& writer)
424 writer.write("dead-zone", dead_zone);
425 writer.write("jump-with-up", jump_with_up_joy);
427 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
429 writer.start_list("map");
430 writer.write("button", i->first.second);
431 writer.write("control", Controller::controlNames[i->second]);
432 writer.end_list("map");
435 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
436 writer.start_list("map");
437 writer.write("hat", i->first.second);
438 writer.write("control", Controller::controlNames[i->second]);
439 writer.end_list("map");
442 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
443 writer.start_list("map");
444 writer.write("axis", i->first.second);
445 writer.write("control", Controller::controlNames[i->second]);
446 writer.end_list("map");
451 JoystickManager::set_joy_controls(Controller::Control id, bool value)
453 if (jump_with_up_joy && id == Controller::UP)
455 parent->get_controller()->set_control(Controller::JUMP, value);
458 parent->get_controller()->set_control(id, value);