2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
3 // 2007 Ingo Ruhnke <grumbel@gmx.de>
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "control/joystickkeyboardcontroller.hpp"
22 #include "lisp/list_iterator.hpp"
23 #include "gui/menu_manager.hpp"
24 #include "supertux/console.hpp"
25 #include "supertux/gameconfig.hpp"
26 #include "supertux/menu/joystick_menu.hpp"
27 #include "supertux/menu/keyboard_menu.hpp"
28 #include "util/gettext.hpp"
29 #include "util/writer.hpp"
31 JoystickKeyboardController::JoystickKeyboardController() :
47 wait_for_joystick(-1),
49 joystick_options_menu(0)
51 // initialize default keyboard map
52 keymap[SDLK_LEFT] = LEFT;
53 keymap[SDLK_RIGHT] = RIGHT;
55 keymap[SDLK_DOWN] = DOWN;
56 keymap[SDLK_SPACE] = JUMP;
57 keymap[SDLK_LCTRL] = ACTION;
58 keymap[SDLK_LALT] = ACTION;
59 keymap[SDLK_ESCAPE] = PAUSE_MENU;
60 keymap[SDLK_p] = PAUSE_MENU;
61 keymap[SDLK_PAUSE] = PAUSE_MENU;
62 keymap[SDLK_RETURN] = MENU_SELECT;
63 keymap[SDLK_KP_ENTER] = MENU_SELECT;
64 keymap[SDLK_CARET] = CONSOLE;
65 keymap[SDLK_DELETE] = PEEK_LEFT;
66 keymap[SDLK_PAGEDOWN] = PEEK_RIGHT;
67 keymap[SDLK_HOME] = PEEK_UP;
68 keymap[SDLK_END] = PEEK_DOWN;
70 jump_with_up_joy = false;
71 jump_with_up_kbd = false;
73 updateAvailableJoysticks();
77 // Default joystick button configuration
78 joy_button_map[0] = JUMP;
79 joy_button_map[1] = ACTION;
81 if( min_joybuttons > 5 ){
82 joy_button_map[4] = PEEK_LEFT;
83 joy_button_map[5] = PEEK_RIGHT;
85 if(min_joybuttons > 7)
86 joy_button_map[min_joybuttons-1] = PAUSE_MENU;
88 // map the last 2 buttons to menu and pause
89 if(min_joybuttons > 2)
90 joy_button_map[min_joybuttons-1] = PAUSE_MENU;
91 // map all remaining joystick buttons to MENU_SELECT
92 for(int i = 2; i < max_joybuttons; ++i) {
93 if(i != min_joybuttons-1)
94 joy_button_map[i] = MENU_SELECT;
98 // Default joystick axis configuration
99 joy_axis_map[-1] = LEFT;
100 joy_axis_map[ 1] = RIGHT;
101 joy_axis_map[-2] = UP;
102 joy_axis_map[ 2] = DOWN;
105 JoystickKeyboardController::~JoystickKeyboardController()
107 for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
108 i != joysticks.end(); ++i) {
110 SDL_JoystickClose(*i);
113 delete key_options_menu;
114 delete joystick_options_menu;
118 JoystickKeyboardController::updateAvailableJoysticks()
120 for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
121 i != joysticks.end(); ++i) {
123 SDL_JoystickClose(*i);
127 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
128 SDL_InitSubSystem(SDL_INIT_JOYSTICK);
130 int joystick_count = SDL_NumJoysticks();
136 if( joystick_count > 0 ){
137 for(int i = 0; i < joystick_count; ++i) {
138 SDL_Joystick* joystick = SDL_JoystickOpen(i);
140 if(SDL_JoystickNumButtons(joystick) < 2) {
141 log_info << "Joystick " << i << ": " << SDL_JoystickName(i) << " has less than 2 buttons" << std::endl;
144 if(SDL_JoystickNumAxes(joystick) < 2
145 && SDL_JoystickNumHats(joystick) == 0) {
146 log_info << "Joystick " << i << ": " << SDL_JoystickName(i) << " has less than 2 axes and no hat" << std::endl;
150 SDL_JoystickClose(joystick);
154 if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
155 min_joybuttons = SDL_JoystickNumButtons(joystick);
157 if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
158 max_joybuttons = SDL_JoystickNumButtons(joystick);
160 if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
161 max_joyaxis = SDL_JoystickNumAxes(joystick);
163 if(SDL_JoystickNumHats(joystick) > max_joyhats)
164 max_joyhats = SDL_JoystickNumHats(joystick);
166 joysticks.push_back(joystick);
170 // some joysticks or SDL seem to produce some bogus events after being opened
171 Uint32 ticks = SDL_GetTicks();
172 while(SDL_GetTicks() - ticks < 200) {
174 SDL_PollEvent(&event);
179 JoystickKeyboardController::read(const Reader& lisp)
181 const lisp::Lisp* keymap_lisp = lisp.get_lisp("keymap");
184 keymap_lisp->get("jump-with-up", jump_with_up_kbd);
185 lisp::ListIterator iter(keymap_lisp);
187 if(iter.item() == "map") {
190 const lisp::Lisp* map = iter.lisp();
191 map->get("key", key);
192 map->get("control", control);
193 if(key < SDLK_FIRST || key >= SDLK_LAST) {
194 log_info << "Invalid key '" << key << "' in keymap" << std::endl;
199 for(i = 0; controlNames[i] != 0; ++i) {
200 if(control == controlNames[i])
203 if(controlNames[i] == 0) {
204 log_info << "Invalid control '" << control << "' in keymap" << std::endl;
207 keymap[(SDLKey) key] = (Control)i;
212 const lisp::Lisp* joystick_lisp = lisp.get_lisp("joystick");
214 joystick_lisp->get("dead-zone", dead_zone);
215 joystick_lisp->get("jump-with-up", jump_with_up_joy);
216 lisp::ListIterator iter(joystick_lisp);
218 if(iter.item() == "map") {
223 const lisp::Lisp* map = iter.lisp();
225 map->get("control", control);
227 for(i = 0; controlNames[i] != 0; ++i) {
228 if(control == controlNames[i])
231 if(controlNames[i] == 0) {
232 log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
236 if (map->get("button", button)) {
237 if(button < 0 || button >= max_joybuttons) {
238 log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
241 bind_joybutton(button, (Control) i);
244 if (map->get("axis", axis)) {
245 if (axis == 0 || abs(axis) > max_joyaxis) {
246 log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
249 bind_joyaxis(axis, (Control) i);
252 if (map->get("hat", hat)) {
253 if (hat != SDL_HAT_UP &&
254 hat != SDL_HAT_DOWN &&
255 hat != SDL_HAT_LEFT &&
256 hat != SDL_HAT_RIGHT) {
257 log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
260 bind_joyhat(hat, (Control) i);
269 JoystickKeyboardController::write(Writer& writer)
271 writer.start_list("keymap");
272 writer.write("jump-with-up", jump_with_up_kbd);
273 for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
274 writer.start_list("map");
275 writer.write("key", (int) i->first);
276 writer.write("control", controlNames[i->second]);
277 writer.end_list("map");
279 writer.end_list("keymap");
281 writer.start_list("joystick");
282 writer.write("dead-zone", dead_zone);
283 writer.write("jump-with-up", jump_with_up_joy);
285 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
287 writer.start_list("map");
288 writer.write("button", i->first);
289 writer.write("control", controlNames[i->second]);
290 writer.end_list("map");
293 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
294 writer.start_list("map");
295 writer.write("hat", i->first);
296 writer.write("control", controlNames[i->second]);
297 writer.end_list("map");
300 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
301 writer.start_list("map");
302 writer.write("axis", i->first);
303 writer.write("control", controlNames[i->second]);
304 writer.end_list("map");
307 writer.end_list("joystick");
311 JoystickKeyboardController::reset()
317 JoystickKeyboardController::set_joy_controls(Control id, bool value)
319 if (jump_with_up_joy && id == Controller::UP)
320 controls[Controller::JUMP] = value;
322 controls[(Control)id] = value;
326 JoystickKeyboardController::process_event(const SDL_Event& event)
331 process_key_event(event);
334 case SDL_JOYAXISMOTION:
335 process_axis_event(event.jaxis);
338 case SDL_JOYHATMOTION:
339 process_hat_event(event.jhat);
342 case SDL_JOYBUTTONDOWN:
343 case SDL_JOYBUTTONUP:
344 process_button_event(event.jbutton);
353 JoystickKeyboardController::process_button_event(const SDL_JoyButtonEvent& jbutton)
355 if(wait_for_joystick >= 0)
357 if(jbutton.state == SDL_PRESSED)
359 bind_joybutton(jbutton.button, (Control)wait_for_joystick);
360 joystick_options_menu->update();
362 wait_for_joystick = -1;
367 ButtonMap::iterator i = joy_button_map.find(jbutton.button);
368 if(i == joy_button_map.end()) {
369 log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
371 set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
377 JoystickKeyboardController::process_axis_event(const SDL_JoyAxisEvent& jaxis)
379 if (wait_for_joystick >= 0)
381 if (abs(jaxis.value) > dead_zone) {
383 bind_joyaxis(-(jaxis.axis + 1), Control(wait_for_joystick));
385 bind_joyaxis(jaxis.axis + 1, Control(wait_for_joystick));
387 joystick_options_menu->update();
388 wait_for_joystick = -1;
393 // Split the axis into left and right, so that both can be
394 // mapped separately (needed for jump/down vs up/down)
395 int axis = jaxis.axis + 1;
397 AxisMap::iterator left = joy_axis_map.find(-axis);
398 AxisMap::iterator right = joy_axis_map.find(axis);
400 if(left == joy_axis_map.end()) {
401 std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
403 if (jaxis.value < -dead_zone)
404 set_joy_controls(left->second, true);
405 else if (jaxis.value > dead_zone)
406 set_joy_controls(left->second, false);
408 set_joy_controls(left->second, false);
411 if(right == joy_axis_map.end()) {
412 std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
414 if (jaxis.value < -dead_zone)
415 set_joy_controls(right->second, false);
416 else if (jaxis.value > dead_zone)
417 set_joy_controls(right->second, true);
419 set_joy_controls(right->second, false);
425 JoystickKeyboardController::process_hat_event(const SDL_JoyHatEvent& jhat)
427 Uint8 changed = hat_state ^ jhat.value;
429 if (wait_for_joystick >= 0)
431 if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
432 bind_joyhat(SDL_HAT_UP, (Control)wait_for_joystick);
434 if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
435 bind_joyhat(SDL_HAT_DOWN, (Control)wait_for_joystick);
437 if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
438 bind_joyhat(SDL_HAT_LEFT, (Control)wait_for_joystick);
440 if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
441 bind_joyhat(SDL_HAT_RIGHT, (Control)wait_for_joystick);
443 joystick_options_menu->update();
444 wait_for_joystick = -1;
448 if (changed & SDL_HAT_UP)
450 HatMap::iterator it = joy_hat_map.find(SDL_HAT_UP);
451 if (it != joy_hat_map.end())
452 set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
455 if (changed & SDL_HAT_DOWN)
457 HatMap::iterator it = joy_hat_map.find(SDL_HAT_DOWN);
458 if (it != joy_hat_map.end())
459 set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
462 if (changed & SDL_HAT_LEFT)
464 HatMap::iterator it = joy_hat_map.find(SDL_HAT_LEFT);
465 if (it != joy_hat_map.end())
466 set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
469 if (changed & SDL_HAT_RIGHT)
471 HatMap::iterator it = joy_hat_map.find(SDL_HAT_RIGHT);
472 if (it != joy_hat_map.end())
473 set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
477 hat_state = jhat.value;
481 JoystickKeyboardController::process_key_event(const SDL_Event& event)
483 KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
485 // if console key was pressed: toggle console
486 if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
487 if (event.type == SDL_KEYDOWN)
488 Console::instance->toggle();
490 if (Console::instance->hasFocus()) {
491 // if console is open: send key there
492 process_console_key_event(event);
493 } else if (MenuManager::current()) {
494 // if menu mode: send key there
495 process_menu_key_event(event);
496 } else if(key_mapping == keymap.end()) {
497 // default action: update controls
498 //log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
500 Control control = key_mapping->second;
501 controls[control] = (event.type == SDL_KEYDOWN);
502 if (jump_with_up_kbd && control == UP){
503 controls[JUMP] = (event.type == SDL_KEYDOWN);
510 JoystickKeyboardController::process_console_key_event(const SDL_Event& event)
512 if (event.type != SDL_KEYDOWN) return;
514 switch (event.key.keysym.sym) {
516 Console::instance->enter();
519 Console::instance->backspace();
522 Console::instance->autocomplete();
525 Console::instance->scroll(-1);
528 Console::instance->scroll(+1);
531 Console::instance->move_cursor(-65535);
534 Console::instance->move_cursor(+65535);
537 Console::instance->show_history(-1);
540 Console::instance->show_history(+1);
543 Console::instance->move_cursor(-1);
546 Console::instance->move_cursor(+1);
549 int c = event.key.keysym.unicode;
550 if ((c >= 32) && (c <= 126)) {
551 Console::instance->input((char)c);
558 JoystickKeyboardController::process_menu_key_event(const SDL_Event& event)
560 // wait for key mode?
561 if(wait_for_key >= 0) {
562 if(event.type == SDL_KEYUP)
565 if(event.key.keysym.sym != SDLK_ESCAPE
566 && event.key.keysym.sym != SDLK_PAUSE) {
567 bind_key(event.key.keysym.sym, (Control) wait_for_key);
570 key_options_menu->update();
574 if(wait_for_joystick >= 0) {
575 if(event.key.keysym.sym == SDLK_ESCAPE) {
577 joystick_options_menu->update();
578 wait_for_joystick = -1;
584 /* we use default keys when the menu is open (to avoid problems when
585 * redefining keys to invalid settings
587 switch(event.key.keysym.sym) {
603 control = MENU_SELECT;
607 control = PAUSE_MENU;
614 controls[control] = (event.type == SDL_KEYDOWN);
618 JoystickKeyboardController::unbind_joystick_control(Control control)
620 // remove all previous mappings for that control
621 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
622 if(i->second == control)
623 joy_axis_map.erase(i++);
628 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
629 if(i->second == control)
630 joy_button_map.erase(i++);
635 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); /* no ++i */) {
636 if(i->second == control)
637 joy_hat_map.erase(i++);
644 JoystickKeyboardController::bind_joyaxis(int axis, Control control)
646 // axis isn't the SDL axis number, but axisnumber + 1 with sign
647 // changed depending on if the positive or negative end is to be
648 // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
651 unbind_joystick_control(control);
654 joy_axis_map[axis] = control;
658 JoystickKeyboardController::bind_joyhat(int dir, Control c)
660 unbind_joystick_control(c);
663 joy_hat_map[dir] = c;
667 JoystickKeyboardController::bind_joybutton(int button, Control control)
669 unbind_joystick_control(control);
672 joy_button_map[button] = control;
676 JoystickKeyboardController::bind_key(SDLKey key, Control control)
678 // remove all previous mappings for that control and for that key
679 for(KeyMap::iterator i = keymap.begin();
680 i != keymap.end(); /* no ++i */) {
681 if(i->second == control) {
682 KeyMap::iterator e = i;
690 KeyMap::iterator i = keymap.find(key);
691 if(i != keymap.end())
695 keymap[key]= control;
699 JoystickKeyboardController::print_joystick_mappings()
701 std::cout << "Joystick Mappings" << std::endl;
702 std::cout << "-----------------" << std::endl;
703 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
704 std::cout << "Axis: " << i->first << " -> " << i->second << std::endl;
707 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
708 std::cout << "Button: " << i->first << " -> " << i->second << std::endl;
711 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
712 std::cout << "Hat: " << i->first << " -> " << i->second << std::endl;
714 std::cout << std::endl;
718 JoystickKeyboardController::reversemap_key(Control c)
720 for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
729 JoystickKeyboardController::reversemap_joyaxis(Control c)
731 for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
740 JoystickKeyboardController::reversemap_joybutton(Control c)
742 for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
751 JoystickKeyboardController::reversemap_joyhat(Control c)
753 for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
762 JoystickKeyboardController::get_key_options_menu()
764 if(key_options_menu == 0) {
765 key_options_menu = new KeyboardMenu(this);
768 return key_options_menu;
772 JoystickKeyboardController::get_joystick_options_menu()
774 if(joystick_options_menu == 0) {
775 joystick_options_menu = new JoystickMenu(this);
778 return joystick_options_menu;