- general cleanup
[supertux.git] / src / control / joystickkeyboardcontroller.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
5 //                2007 Ingo Ruhnke <grumbel@gmx.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 #include <config.h>
22
23 #include <sstream>
24 #include "joystickkeyboardcontroller.hpp"
25 #include "log.hpp"
26 #include "gui/menu.hpp"
27 #include "gettext.hpp"
28 #include "lisp/lisp.hpp"
29 #include "lisp/list_iterator.hpp"
30 #include "game_session.hpp"
31 #include "console.hpp"
32 #include "gameconfig.hpp"
33
34 class JoystickKeyboardController::JoystickMenu : public Menu
35 {
36 public:
37   JoystickMenu(JoystickKeyboardController* controller);
38   virtual ~JoystickMenu();
39
40   void update();
41   std::string get_button_name(int button);
42   void update_menu_item(Control id);
43   virtual void menu_action(MenuItem* item);
44   JoystickKeyboardController* controller;
45 };
46
47 class JoystickKeyboardController::KeyboardMenu : public Menu
48 {
49 public:
50   KeyboardMenu(JoystickKeyboardController* controller);
51   ~KeyboardMenu();
52
53   void update();
54   std::string get_key_name(SDLKey key);
55   virtual void menu_action(MenuItem* item);
56   JoystickKeyboardController* controller;
57 };
58
59 JoystickKeyboardController::JoystickKeyboardController()
60   : hat_state(0),
61     wait_for_key(-1), wait_for_joystick(-1),
62     key_options_menu(0), joystick_options_menu(0)
63 {
64   // initialize default keyboard map
65   keymap[SDLK_LEFT]     = LEFT;
66   keymap[SDLK_RIGHT]    = RIGHT;
67   keymap[SDLK_UP]       = UP;
68   keymap[SDLK_DOWN]     = DOWN;
69   keymap[SDLK_SPACE]    = JUMP;
70   keymap[SDLK_LCTRL]    = ACTION;
71   keymap[SDLK_LALT]     = ACTION;
72   keymap[SDLK_ESCAPE]   = PAUSE_MENU;
73   keymap[SDLK_p]        = PAUSE_MENU;
74   keymap[SDLK_PAUSE]    = PAUSE_MENU;
75   keymap[SDLK_RETURN]   = MENU_SELECT;
76   keymap[SDLK_KP_ENTER] = MENU_SELECT;
77   keymap[SDLK_CARET]    = CONSOLE;
78   keymap[SDLK_DELETE]   = PEEK_LEFT;
79   keymap[SDLK_END]      = PEEK_RIGHT;
80
81   int joystick_count = SDL_NumJoysticks();
82   min_joybuttons = -1;
83   max_joybuttons = -1;
84   max_joyaxis    = -1;
85   max_joyhats    = -1;
86
87   for(int i = 0; i < joystick_count; ++i) {
88     SDL_Joystick* joystick = SDL_JoystickOpen(i);
89     bool good = true;
90     if(SDL_JoystickNumButtons(joystick) < 2) {
91       log_info << "Joystick " << i << " has less than 2 buttons" << std::endl;
92       good = false;
93     }
94     if(SDL_JoystickNumAxes(joystick) < 2
95        && SDL_JoystickNumHats(joystick) == 0) {
96       log_info << "Joystick " << i << " has less than 2 axes and no hat" << std::endl;
97       good = false;
98     }
99     if(!good) {
100       SDL_JoystickClose(joystick);
101       continue;
102     }
103
104     if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
105       min_joybuttons = SDL_JoystickNumButtons(joystick);
106
107     if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
108       max_joybuttons = SDL_JoystickNumButtons(joystick);
109
110     if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
111       max_joyaxis = SDL_JoystickNumAxes(joystick);
112
113     if(SDL_JoystickNumHats(joystick) > max_joyhats)
114       max_joyhats = SDL_JoystickNumHats(joystick);
115
116     joysticks.push_back(joystick);
117   }
118
119   dead_zone = 1000;
120
121   // Default joystick button configuration
122   joy_button_map[0] = JUMP;
123   joy_button_map[1] = ACTION;
124   // 6 or more Buttons
125   if( min_joybuttons > 5 ){
126     joy_button_map[4] = PEEK_LEFT;
127     joy_button_map[5] = PEEK_RIGHT;
128     // 8 or more
129     if(min_joybuttons > 7)
130       joy_button_map[min_joybuttons-1] = PAUSE_MENU;
131   } else {
132     // map the last 2 buttons to menu and pause
133     if(min_joybuttons > 2)
134       joy_button_map[min_joybuttons-1] = PAUSE_MENU;
135     // map all remaining joystick buttons to MENU_SELECT
136     for(int i = 2; i < max_joybuttons; ++i) {
137       if(i != min_joybuttons-1)
138         joy_button_map[i] = MENU_SELECT;
139     }
140   }
141
142   // Default joystick axis configuration
143   joy_axis_map[-1] = LEFT;
144   joy_axis_map[ 1] = RIGHT;
145   joy_axis_map[-2] = UP;
146   joy_axis_map[ 2] = DOWN;
147
148   // some joysticks or SDL seem to produce some bogus events after being opened
149   Uint32 ticks = SDL_GetTicks();
150   while(SDL_GetTicks() - ticks < 200) {
151     SDL_Event event;
152     SDL_PollEvent(&event);
153   }
154 }
155
156 JoystickKeyboardController::~JoystickKeyboardController()
157 {
158   for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
159       i != joysticks.end(); ++i) {
160     if(*i != 0)
161       SDL_JoystickClose(*i);
162   }
163
164   delete key_options_menu;
165   delete joystick_options_menu;
166 }
167
168 void
169 JoystickKeyboardController::read(const lisp::Lisp& lisp)
170 {
171   const lisp::Lisp* keymap_lisp = lisp.get_lisp("keymap");
172   if(keymap_lisp) {
173     keymap.clear();
174     lisp::ListIterator iter(keymap_lisp);
175     while(iter.next()) {
176       if(iter.item() == "map") {
177         int key = -1;
178         std::string control;
179         const lisp::Lisp* map = iter.lisp();
180         map->get("key", key);
181         map->get("control", control);
182         if(key < SDLK_FIRST || key >= SDLK_LAST) {
183           log_info << "Invalid key '" << key << "' in keymap" << std::endl;
184           continue;
185         }
186
187         int i = 0;
188         for(i = 0; controlNames[i] != 0; ++i) {
189           if(control == controlNames[i])
190             break;
191         }
192         if(controlNames[i] == 0) {
193           log_info << "Invalid control '" << control << "' in keymap" << std::endl;
194           continue;
195         }
196         keymap[(SDLKey) key] = (Control)i;
197       } else {
198         log_info << "Invalid lisp element '" << iter.item() << "' in keymap" << std::endl;
199       }
200     }
201   }
202
203   const lisp::Lisp* joystick_lisp = lisp.get_lisp("joystick");
204   if(joystick_lisp) {
205     joystick_lisp->get("dead_zone", dead_zone);
206     lisp::ListIterator iter(joystick_lisp);
207     while(iter.next()) {
208       if(iter.item() == "map") {
209         int button = -1;
210         int axis   = 0;
211         int hat    = -1;
212         std::string control;
213         const lisp::Lisp* map = iter.lisp();
214
215         map->get("control", control);
216         int i = 0;
217         for(i = 0; controlNames[i] != 0; ++i) {
218           if(control == controlNames[i])
219             break;
220         }
221         if(controlNames[i] == 0) {
222           log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
223           continue;
224         }
225
226         if (map->get("button", button)) {
227           if(button < 0 || button >= max_joybuttons) {
228             log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
229             continue;
230           }
231           bind_joybutton(button, (Control) i);
232         }
233
234         if (map->get("axis",   axis)) {
235           if (axis == 0 || abs(axis) > max_joyaxis) {
236             log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
237             continue;
238           }
239           bind_joyaxis(axis, (Control) i);
240         }
241
242         if (map->get("hat",   hat)) {
243           if (hat != SDL_HAT_UP   &&
244               hat != SDL_HAT_DOWN &&
245               hat != SDL_HAT_LEFT &&
246               hat != SDL_HAT_RIGHT) {
247             log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
248             continue;
249           } else {
250             bind_joyhat(hat, (Control) i);
251           }
252         }
253       }
254     }
255   }
256 }
257
258 void
259 JoystickKeyboardController::write(lisp::Writer& writer)
260 {
261   writer.start_list("keymap");
262   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
263     writer.start_list("map");
264     writer.write_int("key", (int) i->first);
265     writer.write_string("control", controlNames[i->second]);
266     writer.end_list("map");
267   }
268   writer.end_list("keymap");
269
270   writer.start_list("joystick");
271   writer.write_int("dead_zone", dead_zone);
272
273   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
274       ++i) {
275     writer.start_list("map");
276     writer.write_int("button", i->first);
277     writer.write_string("control", controlNames[i->second]);
278     writer.end_list("map");
279   }
280
281   for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
282     writer.start_list("map");
283     writer.write_int("hat", i->first);
284     writer.write_string("control", controlNames[i->second]);
285     writer.end_list("map");
286   }
287
288   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
289     writer.start_list("map");
290     writer.write_int("axis", i->first);
291     writer.write_string("control", controlNames[i->second]);
292     writer.end_list("map");
293   }
294
295   writer.end_list("joystick");
296 }
297
298 void
299 JoystickKeyboardController::reset()
300 {
301   Controller::reset();
302 }
303
304 void
305 JoystickKeyboardController::process_event(const SDL_Event& event)
306 {
307   switch(event.type) {
308     case SDL_KEYUP:
309     case SDL_KEYDOWN:
310       process_key_event(event);
311       break;
312
313     case SDL_JOYAXISMOTION:
314       process_axis_event(event.jaxis);
315       break;
316
317     case SDL_JOYHATMOTION:
318       process_hat_event(event.jhat);
319       break;
320
321     case SDL_JOYBUTTONDOWN:
322     case SDL_JOYBUTTONUP:
323       process_button_event(event.jbutton);
324       break;
325
326     default:
327       break;
328   }
329 }
330
331 void
332 JoystickKeyboardController::process_button_event(const SDL_JoyButtonEvent& jbutton)
333 {
334   if(wait_for_joystick >= 0) 
335     {
336       if(jbutton.state == SDL_PRESSED)
337         {
338           bind_joybutton(jbutton.button, (Control)wait_for_joystick);
339           joystick_options_menu->update();
340           reset();
341           wait_for_joystick = -1;
342         }
343     } 
344   else 
345     {
346       ButtonMap::iterator i = joy_button_map.find(jbutton.button);
347       if(i == joy_button_map.end()) {
348         log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
349       } else {
350         controls[i->second] = (jbutton.state == SDL_PRESSED);
351       }
352     }
353 }
354
355 void
356 JoystickKeyboardController::process_axis_event(const SDL_JoyAxisEvent& jaxis)
357 {
358   if (wait_for_joystick >= 0)
359     {
360       if (abs(jaxis.value) > dead_zone) {
361         if (jaxis.value < 0)
362           bind_joyaxis(-(jaxis.axis + 1), Control(wait_for_joystick));
363         else
364           bind_joyaxis(jaxis.axis + 1, Control(wait_for_joystick));
365
366         joystick_options_menu->update();
367         wait_for_joystick = -1;
368       }
369     }
370   else
371     {
372       // Split the axis into left and right, so that both can be
373       // mapped seperatly (needed for jump/down vs up/down)
374       int axis = jaxis.axis + 1;
375
376       AxisMap::iterator left  = joy_axis_map.find(-axis);
377       AxisMap::iterator right = joy_axis_map.find(axis);
378
379       if(left == joy_axis_map.end()) {
380         std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
381       } else {
382         if (jaxis.value < -dead_zone)
383           controls[left->second]  = true;
384         else if (jaxis.value > dead_zone)
385           controls[left->second]  = false;
386         else
387           controls[left->second]  = false;
388       }
389
390       if(right == joy_axis_map.end()) {
391         std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
392       } else {
393         if (jaxis.value < -dead_zone)
394           controls[right->second] = false;
395         else if (jaxis.value > dead_zone)
396           controls[right->second] = true;
397         else
398           controls[right->second] = false;
399       }
400     }
401 }
402
403 void
404 JoystickKeyboardController::process_hat_event(const SDL_JoyHatEvent& jhat)
405 {
406   Uint8 changed = hat_state ^ jhat.value;
407
408   if (wait_for_joystick >= 0)
409     {
410       if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
411         bind_joyhat(SDL_HAT_UP, (Control)wait_for_joystick);
412
413       if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
414         bind_joyhat(SDL_HAT_DOWN, (Control)wait_for_joystick);
415
416       if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
417         bind_joyhat(SDL_HAT_LEFT, (Control)wait_for_joystick);
418
419       if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
420         bind_joyhat(SDL_HAT_RIGHT, (Control)wait_for_joystick);
421
422       joystick_options_menu->update();
423       wait_for_joystick = -1;
424     }
425   else
426     {
427       if (changed & SDL_HAT_UP)
428         {
429           HatMap::iterator it = joy_hat_map.find(SDL_HAT_UP);
430           if (it != joy_hat_map.end())
431             controls[it->second] = jhat.value & SDL_HAT_UP;
432         }
433
434       if (changed & SDL_HAT_DOWN)
435         {
436           HatMap::iterator it = joy_hat_map.find(SDL_HAT_DOWN);
437           if (it != joy_hat_map.end())
438             controls[it->second] = jhat.value & SDL_HAT_DOWN;
439         }
440
441       if (changed & SDL_HAT_LEFT)
442         {
443           HatMap::iterator it = joy_hat_map.find(SDL_HAT_LEFT);
444           if (it != joy_hat_map.end())
445             controls[it->second] = jhat.value & SDL_HAT_LEFT;
446         }
447
448       if (changed & SDL_HAT_RIGHT)
449         {
450           HatMap::iterator it = joy_hat_map.find(SDL_HAT_RIGHT);
451           if (it != joy_hat_map.end())
452             controls[it->second] = jhat.value & SDL_HAT_RIGHT;
453         }
454     }
455
456   hat_state = jhat.value;
457 }
458
459 void
460 JoystickKeyboardController::process_key_event(const SDL_Event& event)
461 {
462   KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
463
464   // if console key was pressed: toggle console
465   if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
466     if (event.type == SDL_KEYDOWN) 
467       Console::instance->toggle();
468   } else {
469     if (Console::instance->hasFocus()) {
470       // if console is open: send key there
471       process_console_key_event(event);
472     } else if (Menu::current()) {
473       // if menu mode: send key there
474       process_menu_key_event(event);
475     } else if(key_mapping == keymap.end()) {
476       // default action: update controls
477       log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
478     } else {
479       Control control = key_mapping->second;
480       controls[control] = (event.type == SDL_KEYDOWN);
481     }
482   }
483 }
484
485 void
486 JoystickKeyboardController::process_console_key_event(const SDL_Event& event)
487 {
488   if (event.type != SDL_KEYDOWN) return;
489
490   switch (event.key.keysym.sym) {
491     case SDLK_RETURN:
492       Console::instance->enter();
493       break;
494     case SDLK_BACKSPACE:
495       Console::instance->backspace();
496       break;
497     case SDLK_TAB:
498       Console::instance->autocomplete();
499       break;
500     case SDLK_PAGEUP:
501       Console::instance->scroll(-1);
502       break;
503     case SDLK_PAGEDOWN:
504       Console::instance->scroll(+1);
505       break;
506     case SDLK_HOME:
507       Console::instance->move_cursor(-65535);
508       break;
509     case SDLK_END:
510       Console::instance->move_cursor(+65535);
511       break;
512     case SDLK_UP:
513       Console::instance->show_history(-1);
514       break;
515     case SDLK_DOWN:
516       Console::instance->show_history(+1);
517       break;
518     case SDLK_LEFT:
519       Console::instance->move_cursor(-1);
520       break;
521     case SDLK_RIGHT:
522       Console::instance->move_cursor(+1);
523       break;
524     default:
525       int c = event.key.keysym.unicode;
526       if ((c >= 32) && (c <= 126)) {
527         Console::instance->input((char)c);
528       }
529       break;
530   }
531 }
532
533 void
534 JoystickKeyboardController::process_menu_key_event(const SDL_Event& event)
535 {
536   // wait for key mode?
537   if(wait_for_key >= 0) {
538     if(event.type == SDL_KEYUP)
539       return;
540
541     if(event.key.keysym.sym != SDLK_ESCAPE
542         && event.key.keysym.sym != SDLK_PAUSE) {
543       bind_key(event.key.keysym.sym, (Control) wait_for_key);
544     }
545     reset();
546     key_options_menu->update();
547     wait_for_key = -1;
548     return;
549   }
550   if(wait_for_joystick >= 0) {
551     if(event.key.keysym.sym == SDLK_ESCAPE) {
552       reset();
553       joystick_options_menu->update();
554       wait_for_joystick = -1;
555     }
556     return;
557   }
558
559   Control control;
560   /* we use default keys when the menu is open (to avoid problems when
561    * redefining keys to invalid settings
562    */
563   switch(event.key.keysym.sym) {
564     case SDLK_UP:
565       control = UP;
566       break;
567     case SDLK_DOWN:
568       control = DOWN;
569       break;
570     case SDLK_LEFT:
571       control = LEFT;
572       break;
573     case SDLK_RIGHT:
574       control = RIGHT;
575       break;
576     case SDLK_SPACE:
577     case SDLK_RETURN:
578     case SDLK_KP_ENTER:
579       control = MENU_SELECT;
580       break;
581     case SDLK_ESCAPE:
582     case SDLK_PAUSE:
583       control = PAUSE_MENU;
584       break;
585     default:
586       return;
587       break;
588   }
589
590   controls[control] = (event.type == SDL_KEYDOWN);
591 }
592
593 void
594 JoystickKeyboardController::unbind_joystick_control(Control control)
595 {
596   // remove all previous mappings for that control
597   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
598     if(i->second == control)
599       joy_axis_map.erase(i++);
600     else
601       ++i;
602   }
603
604   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
605     if(i->second == control)
606       joy_button_map.erase(i++);
607     else
608       ++i;
609   }
610
611   for(HatMap::iterator i = joy_hat_map.begin();  i != joy_hat_map.end(); /* no ++i */) {
612     if(i->second == control)
613       joy_hat_map.erase(i++);
614     else
615       ++i;
616   }
617 }
618
619 void
620 JoystickKeyboardController::bind_joyaxis(int axis, Control control)
621 {
622   // axis isn't the SDL axis number, but axisnumber + 1 with sign
623   // changed depending on if the positive or negative end is to be
624   // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
625   // etc.)
626
627   unbind_joystick_control(control);
628
629   // add new mapping
630   joy_axis_map[axis] = control;
631 }
632
633 void
634 JoystickKeyboardController::bind_joyhat(int dir, Control c)
635 {
636   unbind_joystick_control(c);
637
638   // add new mapping
639   joy_hat_map[dir] = c;
640 }
641
642 void
643 JoystickKeyboardController::bind_joybutton(int button, Control control)
644 {
645   unbind_joystick_control(control);
646
647   // add new mapping
648   joy_button_map[button] = control;
649 }
650
651 void
652 JoystickKeyboardController::bind_key(SDLKey key, Control control)
653 {
654   // remove all previous mappings for that control and for that key
655   for(KeyMap::iterator i = keymap.begin();
656       i != keymap.end(); /* no ++i */) {
657     if(i->second == control) {
658       KeyMap::iterator e = i;
659       ++i;
660       keymap.erase(e);
661     } else {
662       ++i;
663     }
664   }
665
666   KeyMap::iterator i = keymap.find(key);
667   if(i != keymap.end())
668     keymap.erase(i);
669
670   // add new mapping
671   keymap[key]= control;
672 }
673
674 void
675 JoystickKeyboardController::print_joystick_mappings()
676 {
677   std::cout << "Joystick Mappings" << std::endl;
678   std::cout << "-----------------" << std::endl;
679   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
680     std::cout << "Axis: " << i->first << " -> " << i->second << std::endl;
681   }
682
683   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
684     std::cout << "Button: " << i->first << " -> " << i->second << std::endl;
685   }
686
687   for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
688     std::cout << "Hat: " << i->first << " -> " << i->second << std::endl;
689   }
690   std::cout << std::endl;
691 }
692
693 SDLKey
694 JoystickKeyboardController::reversemap_key(Control c)
695 {
696   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
697     if(i->second == c)
698       return i->first;
699   }
700
701   return SDLK_UNKNOWN;
702 }
703
704 int
705 JoystickKeyboardController::reversemap_joyaxis(Control c)
706 {
707   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
708     if(i->second == c)
709       return i->first;
710   }
711
712   return 0;
713 }
714
715 int
716 JoystickKeyboardController::reversemap_joybutton(Control c)
717 {
718   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
719     if(i->second == c)
720       return i->first;
721   }
722
723   return -1;
724 }
725
726 int
727 JoystickKeyboardController::reversemap_joyhat(Control c)
728 {
729   for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
730     if(i->second == c)
731       return i->first;
732   }
733
734   return -1;
735 }
736
737 Menu*
738 JoystickKeyboardController::get_key_options_menu()
739 {
740   if(key_options_menu == 0) {
741     key_options_menu = new KeyboardMenu(this);
742   }
743
744   return key_options_menu;
745 }
746
747 Menu*
748 JoystickKeyboardController::get_joystick_options_menu()
749 {
750   if(joystick_options_menu == 0) {
751     joystick_options_menu = new JoystickMenu(this);
752   }
753
754   return joystick_options_menu;
755 }
756
757 //----------------------------------------------------------------------------
758
759 JoystickKeyboardController::KeyboardMenu::KeyboardMenu(
760     JoystickKeyboardController* _controller)
761   : controller(_controller)
762 {
763     add_label(_("Setup Keyboard"));
764     add_hl();
765     add_controlfield(Controller::UP,         _("Up"));
766     add_controlfield(Controller::DOWN,       _("Down"));
767     add_controlfield(Controller::LEFT,       _("Left"));
768     add_controlfield(Controller::RIGHT,      _("Right"));
769     add_controlfield(Controller::JUMP,       _("Jump"));
770     add_controlfield(Controller::ACTION,     _("Action"));
771     add_controlfield(Controller::PEEK_LEFT,  _("Peek Left"));
772     add_controlfield(Controller::PEEK_RIGHT, _("Peek Right"));
773     if (config->console_enabled) {
774       add_controlfield(Controller::CONSOLE, _("Console"));
775     }
776     add_hl();
777     add_back(_("Back"));
778     update();
779 }
780
781 JoystickKeyboardController::KeyboardMenu::~KeyboardMenu()
782 {}
783
784 std::string
785 JoystickKeyboardController::KeyboardMenu::get_key_name(SDLKey key)
786 {
787   switch(key) {
788     case SDLK_UNKNOWN:
789       return _("None");
790     case SDLK_UP:
791       return _("Up cursor");
792     case SDLK_DOWN:
793       return _("Down cursor");
794     case SDLK_LEFT:
795       return _("Left cursor");
796     case SDLK_RIGHT:
797       return _("Right cursor");
798     case SDLK_RETURN:
799       return _("Return");
800     case SDLK_SPACE:
801       return _("Space");
802     case SDLK_RSHIFT:
803       return _("Right Shift");
804     case SDLK_LSHIFT:
805       return _("Left Shift");
806     case SDLK_RCTRL:
807       return _("Right Control");
808     case SDLK_LCTRL:
809       return _("Left Control");
810     case SDLK_RALT:
811       return _("Right Alt");
812     case SDLK_LALT:
813       return _("Left Alt");
814     default:
815       return SDL_GetKeyName((SDLKey) key);
816   }
817 }
818
819 void
820 JoystickKeyboardController::KeyboardMenu::menu_action(MenuItem* item)
821 {
822   assert(item->id >= 0 && item->id < Controller::CONTROLCOUNT);
823   item->change_input(_("Press Key"));
824   controller->wait_for_key = item->id;
825 }
826
827 void
828 JoystickKeyboardController::KeyboardMenu::update()
829 {
830   // update menu
831   get_item_by_id((int) Controller::UP).change_input(get_key_name(
832     controller->reversemap_key(Controller::UP)));
833   get_item_by_id((int) Controller::DOWN).change_input(get_key_name(
834     controller->reversemap_key(Controller::DOWN)));
835   get_item_by_id((int) Controller::LEFT).change_input(get_key_name(
836     controller->reversemap_key(Controller::LEFT)));
837   get_item_by_id((int) Controller::RIGHT).change_input(get_key_name(
838     controller->reversemap_key(Controller::RIGHT)));
839   get_item_by_id((int) Controller::JUMP).change_input(get_key_name(
840     controller->reversemap_key(Controller::JUMP)));
841   get_item_by_id((int) Controller::ACTION).change_input(get_key_name(
842     controller->reversemap_key(Controller::ACTION)));
843   get_item_by_id((int) Controller::PEEK_LEFT).change_input(get_key_name(
844     controller->reversemap_key(Controller::PEEK_LEFT)));
845   get_item_by_id((int) Controller::PEEK_RIGHT).change_input(get_key_name(
846     controller->reversemap_key(Controller::PEEK_RIGHT)));
847   if (config->console_enabled) {
848     get_item_by_id((int) Controller::CONSOLE).change_input(get_key_name(
849       controller->reversemap_key(Controller::CONSOLE)));
850   }
851 }
852
853 //---------------------------------------------------------------------------
854
855 JoystickKeyboardController::JoystickMenu::JoystickMenu(
856   JoystickKeyboardController* _controller)
857   : controller(_controller)
858 {
859   add_label(_("Setup Joystick"));
860   add_hl();
861   if(controller->joysticks.size() > 0) {
862     add_controlfield(Controller::UP,          _("Up"));
863     add_controlfield(Controller::DOWN,        _("Down"));
864     add_controlfield(Controller::LEFT,        _("Left"));
865     add_controlfield(Controller::RIGHT,       _("Right"));
866     add_controlfield(Controller::JUMP,        _("Jump"));
867     add_controlfield(Controller::ACTION,      _("Action"));
868     add_controlfield(Controller::PAUSE_MENU,  _("Pause/Menu"));
869     add_controlfield(Controller::PEEK_LEFT,   _("Peek Left"));
870     add_controlfield(Controller::PEEK_RIGHT,  _("Peek Right"));
871   } else {
872     add_deactive(-1, _("No Joysticks found"));
873   }
874   add_hl();
875   add_back(_("Back"));
876   update();
877 }
878
879 JoystickKeyboardController::JoystickMenu::~JoystickMenu()
880 {}
881
882 std::string
883 JoystickKeyboardController::JoystickMenu::get_button_name(int button)
884 {
885   if(button < 0)
886     return _("None");
887
888   std::ostringstream name;
889   name << "Button " << button;
890   return name.str();
891 }
892
893 void
894 JoystickKeyboardController::JoystickMenu::menu_action(MenuItem* item)
895 {
896   assert(item->id >= 0 && item->id < Controller::CONTROLCOUNT);
897   item->change_input(_("Press Button"));
898   controller->wait_for_joystick = item->id;
899 }
900
901 void
902 JoystickKeyboardController::JoystickMenu::update_menu_item(Control id)
903 {
904   int button  = controller->reversemap_joybutton(id);
905   int axis    = controller->reversemap_joyaxis(id);
906   int hat_dir = controller->reversemap_joyhat(id);
907
908   if (button != -1) {
909     get_item_by_id((int)id).change_input(get_button_name(button));
910   } else if (axis != 0) {
911     std::ostringstream name;
912
913     name << "Axis ";
914
915     if (axis < 0)
916       name << "-";
917     else
918       name << "+";
919
920     if (abs(axis) == 1)
921       name << "X";
922     else if (abs(axis) == 2)
923       name << "Y";
924     else if (abs(axis) == 2)
925       name << "X2";
926     else if (abs(axis) == 3)
927       name << "Y2";
928     else
929       name << abs(axis);
930
931     get_item_by_id((int)id).change_input(name.str());
932   } else if (hat_dir != -1) {
933     std::string name;
934
935     switch (hat_dir)
936       {
937         case SDL_HAT_UP:
938           name = "Hat Up";
939           break;
940
941         case SDL_HAT_DOWN:
942           name = "Hat Down";
943           break;
944
945         case SDL_HAT_LEFT:
946           name = "Hat Left";
947           break;
948
949         case SDL_HAT_RIGHT:
950           name = "Hat Right";
951           break;
952
953         default:
954           name = "Unknown hat_dir";
955           break;
956       }
957
958     get_item_by_id((int)id).change_input(name);
959   } else {
960     get_item_by_id((int)id).change_input("None");
961   }
962 }
963
964 void
965 JoystickKeyboardController::JoystickMenu::update()
966 {
967   if(controller->joysticks.size() == 0)
968     return;
969
970   update_menu_item(Controller::UP);
971   update_menu_item(Controller::DOWN);
972   update_menu_item(Controller::LEFT);
973   update_menu_item(Controller::RIGHT);
974
975   update_menu_item(Controller::JUMP);
976   update_menu_item(Controller::ACTION);
977   update_menu_item(Controller::PAUSE_MENU);
978   update_menu_item(Controller::PEEK_LEFT);
979   update_menu_item(Controller::PEEK_RIGHT);
980 }