Some minor code cleanup resulting from a user running code through cppcheck, mostly...
[supertux.git] / src / control / joystickkeyboardcontroller.cpp
index ba78bf9..aa05152 100644 (file)
@@ -1,13 +1,11 @@
-//  $Id$
-//
 //  SuperTux
 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
 //                2007 Ingo Ruhnke <grumbel@gmx.de>
 //
-//  This program is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU General Public License
-//  as published by the Free Software Foundation; either version 2
-//  of the License, or (at your option) any later version.
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
 //
 //  This program is distributed in the hope that it will be useful,
 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
 //  GNU General Public License for more details.
 //
 //  You should have received a copy of the GNU General Public License
-//  along with this program; if not, write to the Free Software
-//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-#include <config.h>
+#include "control/joystickkeyboardcontroller.hpp"
 
-#include <sstream>
-#include "joystickkeyboardcontroller.hpp"
-#include "log.hpp"
-#include "gui/menu.hpp"
-#include "gettext.hpp"
-#include "lisp/lisp.hpp"
-#include "lisp/list_iterator.hpp"
-#include "game_session.hpp"
-#include "console.hpp"
-#include "gameconfig.hpp"
+#include <iostream>
 
-class JoystickKeyboardController::JoystickMenu : public Menu
-{
-public:
-  JoystickMenu(JoystickKeyboardController* controller);
-  virtual ~JoystickMenu();
-
-  void update();
-  std::string get_button_name(int button);
-  void update_menu_item(Control id);
-  virtual void menu_action(MenuItem* item);
-  JoystickKeyboardController* controller;
-};
-
-class JoystickKeyboardController::KeyboardMenu : public Menu
-{
-public:
-  KeyboardMenu(JoystickKeyboardController* controller);
-  ~KeyboardMenu();
-
-  void update();
-  std::string get_key_name(SDLKey key);
-  virtual void menu_action(MenuItem* item);
-  JoystickKeyboardController* controller;
-};
-
-JoystickKeyboardController::JoystickKeyboardController()
-  : wait_for_key(-1), wait_for_joystick(-1),
-    key_options_menu(0), joystick_options_menu(0)
+#include "lisp/list_iterator.hpp"
+#include "gui/menu_manager.hpp"
+#include "supertux/console.hpp"
+#include "supertux/gameconfig.hpp"
+#include "supertux/menu/menu_storage.hpp"
+#include "supertux/menu/joystick_menu.hpp"
+#include "supertux/menu/keyboard_menu.hpp"
+#include "util/gettext.hpp"
+#include "util/writer.hpp"
+
+JoystickKeyboardController::JoystickKeyboardController() :
+  controller(),
+  keymap(),
+  joy_button_map(),
+  joy_axis_map(),
+  joy_hat_map(),
+  joysticks(),
+  name(),
+  dead_zone(),
+  min_joybuttons(),
+  max_joybuttons(),
+  max_joyaxis(),
+  max_joyhats(),
+  hat_state(0),
+  jump_with_up_joy(),
+  jump_with_up_kbd(),
+  wait_for_key(-1), 
+  wait_for_joystick(-1)
 {
-  // initialize default keyboard map
-  keymap.insert(std::make_pair(SDLK_LEFT,     LEFT));
-  keymap.insert(std::make_pair(SDLK_RIGHT,    RIGHT));
-  keymap.insert(std::make_pair(SDLK_UP,       UP));
-  keymap.insert(std::make_pair(SDLK_DOWN,     DOWN));
-  keymap.insert(std::make_pair(SDLK_SPACE,    JUMP));
-  keymap.insert(std::make_pair(SDLK_LCTRL,    ACTION));
-  keymap.insert(std::make_pair(SDLK_LALT,     ACTION));
-  keymap.insert(std::make_pair(SDLK_ESCAPE,   PAUSE_MENU));
-  keymap.insert(std::make_pair(SDLK_p,        PAUSE_MENU));
-  keymap.insert(std::make_pair(SDLK_PAUSE,    PAUSE_MENU));
-  keymap.insert(std::make_pair(SDLK_RETURN,   MENU_SELECT));
-  keymap.insert(std::make_pair(SDLK_KP_ENTER, MENU_SELECT));
-  keymap.insert(std::make_pair(SDLK_CARET,    CONSOLE));
-  keymap.insert(std::make_pair(SDLK_DELETE,   PEEK_LEFT));
-  keymap.insert(std::make_pair(SDLK_END,      PEEK_RIGHT));
-
-  int joystick_count = SDL_NumJoysticks();
-  min_joybuttons = -1;
-  max_joybuttons = -1;
-  max_joyaxis    = -1;
-  for(int i = 0; i < joystick_count; ++i) {
-    SDL_Joystick* joystick = SDL_JoystickOpen(i);
-    bool good = true;
-    if(SDL_JoystickNumButtons(joystick) < 2) {
-      log_info << "Joystick " << i << " has less than 2 buttons" << std::endl;
-      good = false;
-    }
-    if(SDL_JoystickNumAxes(joystick) < 2
-       && SDL_JoystickNumHats(joystick) == 0) {
-      log_info << "Joystick " << i << " has less than 2 axes and no hat" << std::endl;
-      good = false;
-    }
-    if(!good) {
-      SDL_JoystickClose(joystick);
-      continue;
-    }
+  controller = new Controller;
 
-    if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
-      min_joybuttons = SDL_JoystickNumButtons(joystick);
-
-    if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
-      max_joybuttons = SDL_JoystickNumButtons(joystick);
-
-    if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
-      max_joyaxis = SDL_JoystickNumAxes(joystick);
-
-    joysticks.push_back(joystick);
-  }
+  // initialize default keyboard map
+  keymap[SDLK_LEFT]     = Controller::LEFT;
+  keymap[SDLK_RIGHT]    = Controller::RIGHT;
+  keymap[SDLK_UP]       = Controller::UP;
+  keymap[SDLK_DOWN]     = Controller::DOWN;
+  keymap[SDLK_SPACE]    = Controller::JUMP;
+  keymap[SDLK_LCTRL]    = Controller::ACTION;
+  keymap[SDLK_LALT]     = Controller::ACTION;
+  keymap[SDLK_ESCAPE]   = Controller::PAUSE_MENU;
+  keymap[SDLK_p]        = Controller::PAUSE_MENU;
+  keymap[SDLK_PAUSE]    = Controller::PAUSE_MENU;
+  keymap[SDLK_RETURN]   = Controller::MENU_SELECT;
+  keymap[SDLK_KP_ENTER] = Controller::MENU_SELECT;
+  keymap[SDLK_CARET]    = Controller::CONSOLE;
+  keymap[SDLK_DELETE]   = Controller::PEEK_LEFT;
+  keymap[SDLK_PAGEDOWN] = Controller::PEEK_RIGHT;
+  keymap[SDLK_HOME]     = Controller::PEEK_UP;
+  keymap[SDLK_END]      = Controller::PEEK_DOWN;
+
+  jump_with_up_joy = false;
+  jump_with_up_kbd = false;
+
+  updateAvailableJoysticks();
 
-  use_hat = true;
   dead_zone = 1000;
 
   // Default joystick button configuration
-  joy_button_map.insert(std::make_pair(0, JUMP));
-  joy_button_map.insert(std::make_pair(1, ACTION));
+  bind_joybutton(0, 0, Controller::JUMP);
+  bind_joybutton(0, 1, Controller::ACTION);
   // 6 or more Buttons
   if( min_joybuttons > 5 ){
-    joy_button_map.insert(std::make_pair( 4, PEEK_LEFT));
-    joy_button_map.insert(std::make_pair( 5, PEEK_RIGHT));
+    bind_joybutton(0, 4, Controller::PEEK_LEFT);
+    bind_joybutton(0, 5, Controller::PEEK_RIGHT);
     // 8 or more
     if(min_joybuttons > 7)
-      joy_button_map.insert(std::make_pair(min_joybuttons-1, PAUSE_MENU));
-    // map all remaining joystick buttons to MENU_SELECT
-    for(int i = 2; i < max_joybuttons; ++i) {
-      if( i != min_joybuttons-1 && i !=4  && i!= 5 )
-        joy_button_map.insert(std::make_pair(i, MENU_SELECT));
-    }
-
+      bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
   } else {
     // map the last 2 buttons to menu and pause
     if(min_joybuttons > 2)
-      joy_button_map.insert(std::make_pair(min_joybuttons-1, PAUSE_MENU));
+      bind_joybutton(0, min_joybuttons-1, Controller::PAUSE_MENU);
     // map all remaining joystick buttons to MENU_SELECT
     for(int i = 2; i < max_joybuttons; ++i) {
       if(i != min_joybuttons-1)
-        joy_button_map.insert(std::make_pair(i, MENU_SELECT));
+       bind_joybutton(0, i, Controller::MENU_SELECT);
     }
   }
 
   // Default joystick axis configuration
-  joy_axis_map.insert(std::make_pair(-1, LEFT));
-  joy_axis_map.insert(std::make_pair( 1, RIGHT));
-  joy_axis_map.insert(std::make_pair(-2, UP));
-  joy_axis_map.insert(std::make_pair( 2, DOWN));
+  bind_joyaxis(0, -1, Controller::LEFT);
+  bind_joyaxis(0, 1, Controller::RIGHT);
+  bind_joyaxis(0, -2, Controller::UP);
+  bind_joyaxis(0, 2, Controller::DOWN);
+}
 
-  // some joysticks or SDL seem to produce some bogus events after being opened
-  Uint32 ticks = SDL_GetTicks();
-  while(SDL_GetTicks() - ticks < 200) {
-    SDL_Event event;
-    SDL_PollEvent(&event);
+JoystickKeyboardController::~JoystickKeyboardController()
+{
+  for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
+      i != joysticks.end(); ++i) {
+    if(*i != 0)
+      SDL_JoystickClose(*i);
   }
+  delete controller;
 }
 
-JoystickKeyboardController::~JoystickKeyboardController()
+void
+JoystickKeyboardController::updateAvailableJoysticks()
 {
   for(std::vector<SDL_Joystick*>::iterator i = joysticks.begin();
       i != joysticks.end(); ++i) {
     if(*i != 0)
       SDL_JoystickClose(*i);
   }
+  joysticks.clear();
+  
+  SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+  SDL_InitSubSystem(SDL_INIT_JOYSTICK);
+
+  int joystick_count = SDL_NumJoysticks();
+  min_joybuttons = -1;
+  max_joybuttons = -1;
+  max_joyaxis    = -1;
+  max_joyhats    = -1;
+
+  if( joystick_count > 0 ){
+    for(int i = 0; i < joystick_count; ++i) {
+      SDL_Joystick* joystick = SDL_JoystickOpen(i);
+      bool good = true;
+      if(SDL_JoystickNumButtons(joystick) < 2) {
+        log_info << "Joystick " << i << ": " << SDL_JoystickName(i) << " has less than 2 buttons" << std::endl;
+        good = false;
+      }
+      if(SDL_JoystickNumAxes(joystick) < 2
+         && SDL_JoystickNumHats(joystick) == 0) {
+        log_info << "Joystick " << i << ": " << SDL_JoystickName(i) << " has less than 2 axes and no hat" << std::endl;
+        good = false;
+      }
+      if(!good) {
+        SDL_JoystickClose(joystick);
+        continue;
+      }
+
+      if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
+        min_joybuttons = SDL_JoystickNumButtons(joystick);
+
+      if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
+        max_joybuttons = SDL_JoystickNumButtons(joystick);
+
+      if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
+        max_joyaxis = SDL_JoystickNumAxes(joystick);
+
+      if(SDL_JoystickNumHats(joystick) > max_joyhats)
+        max_joyhats = SDL_JoystickNumHats(joystick);
+
+      joysticks.push_back(joystick);
+    }
+  }
 
-  delete key_options_menu;
-  delete joystick_options_menu;
+  // some joysticks or SDL seem to produce some bogus events after being opened
+  Uint32 ticks = SDL_GetTicks();
+  while(SDL_GetTicks() - ticks < 200) {
+    SDL_Event event;
+    SDL_PollEvent(&event);
+  }
+}
+
+Controller*
+JoystickKeyboardController::get_main_controller()
+{
+  return controller;
 }
 
 void
-JoystickKeyboardController::read(const lisp::Lisp& lisp)
+JoystickKeyboardController::read(const Reader& lisp)
 {
   const lisp::Lisp* keymap_lisp = lisp.get_lisp("keymap");
   if(keymap_lisp) {
     keymap.clear();
+    keymap_lisp->get("jump-with-up", jump_with_up_kbd);
     lisp::ListIterator iter(keymap_lisp);
     while(iter.next()) {
       if(iter.item() == "map") {
@@ -186,58 +202,72 @@ JoystickKeyboardController::read(const lisp::Lisp& lisp)
         }
 
         int i = 0;
-        for(i = 0; controlNames[i] != 0; ++i) {
-          if(control == controlNames[i])
+        for(i = 0; Controller::controlNames[i] != 0; ++i) {
+          if(control == Controller::controlNames[i])
             break;
         }
-        if(controlNames[i] == 0) {
+        if(Controller::controlNames[i] == 0) {
           log_info << "Invalid control '" << control << "' in keymap" << std::endl;
           continue;
         }
-        keymap.insert(std::make_pair((SDLKey) key, (Control) i));
-      } else {
-        log_info << "Invalid lisp element '" << iter.item() << "' in keymap" << std::endl;
+        keymap[SDLKey(key)] = Control(i);
       }
     }
   }
 
   const lisp::Lisp* joystick_lisp = lisp.get_lisp("joystick");
   if(joystick_lisp) {
-    joystick_lisp->get("use_hat", use_hat);
-    joystick_lisp->get("dead_zone", dead_zone);
+    joystick_lisp->get("dead-zone", dead_zone);
+    joystick_lisp->get("jump-with-up", jump_with_up_joy);
     lisp::ListIterator iter(joystick_lisp);
     while(iter.next()) {
       if(iter.item() == "map") {
         int button = -1;
         int axis   = 0;
+        int hat    = -1;
         std::string control;
         const lisp::Lisp* map = iter.lisp();
 
         map->get("control", control);
         int i = 0;
-        for(i = 0; controlNames[i] != 0; ++i) {
-          if(control == controlNames[i])
+        for(i = 0; Controller::controlNames[i] != 0; ++i) {
+          if(control == Controller::controlNames[i])
             break;
         }
-        if(controlNames[i] == 0) {
+        if(Controller::controlNames[i] == 0) {
           log_info << "Invalid control '" << control << "' in buttonmap" << std::endl;
           continue;
         }
 
+       bool js_available = joysticks.size() > 0;
+
         if (map->get("button", button)) {
-          if(button < 0 || button >= max_joybuttons) {
+          if(js_available && (button < 0 || button >= max_joybuttons)) {
             log_info << "Invalid button '" << button << "' in buttonmap" << std::endl;
             continue;
           }
-          reset_joybutton(button, (Control) i);
+          bind_joybutton(0, button, Control(i));
         }
 
         if (map->get("axis",   axis)) {
-          if (axis == 0 || abs(axis) > max_joyaxis) {
+          if (js_available && (axis == 0 || abs(axis) > max_joyaxis)) {
+            log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
+            continue;
+          }
+          bind_joyaxis(0, axis, Control(i));
+        }
+
+        if (map->get("hat",   hat)) {
+          if (js_available        &&
+              hat != SDL_HAT_UP   &&
+              hat != SDL_HAT_DOWN &&
+              hat != SDL_HAT_LEFT &&
+              hat != SDL_HAT_RIGHT) {
             log_info << "Invalid axis '" << axis << "' in axismap" << std::endl;
             continue;
+          } else {
+            bind_joyhat(0, hat, Control(i));
           }
-          reset_joyaxis(axis, (Control) i);
         }
       }
     }
@@ -245,33 +275,41 @@ JoystickKeyboardController::read(const lisp::Lisp& lisp)
 }
 
 void
-JoystickKeyboardController::write(lisp::Writer& writer)
+JoystickKeyboardController::write(Writer& writer)
 {
   writer.start_list("keymap");
+  writer.write("jump-with-up", jump_with_up_kbd);
   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
     writer.start_list("map");
-    writer.write_int("key", (int) i->first);
-    writer.write_string("control", controlNames[i->second]);
+    writer.write("key", (int) i->first);
+    writer.write("control", Controller::controlNames[i->second]);
     writer.end_list("map");
   }
   writer.end_list("keymap");
 
   writer.start_list("joystick");
-  writer.write_bool("use_hat", use_hat);
-  writer.write_int("dead_zone", dead_zone);
+  writer.write("dead-zone", dead_zone);
+  writer.write("jump-with-up", jump_with_up_joy);
 
   for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end();
       ++i) {
     writer.start_list("map");
-    writer.write_int("button", i->first);
-    writer.write_string("control", controlNames[i->second]);
+    writer.write("button", i->first.second);
+    writer.write("control", Controller::controlNames[i->second]);
+    writer.end_list("map");
+  }
+
+  for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+    writer.start_list("map");
+    writer.write("hat", i->first.second);
+    writer.write("control", Controller::controlNames[i->second]);
     writer.end_list("map");
   }
 
   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
     writer.start_list("map");
-    writer.write_int("axis", i->first);
-    writer.write_string("control", controlNames[i->second]);
+    writer.write("axis", i->first.second);
+    writer.write("control", Controller::controlNames[i->second]);
     writer.end_list("map");
   }
 
@@ -279,9 +317,24 @@ JoystickKeyboardController::write(lisp::Writer& writer)
 }
 
 void
+JoystickKeyboardController::update()
+{
+  controller->update();
+}
+
+void
 JoystickKeyboardController::reset()
 {
-  Controller::reset();
+  controller->reset();
+}
+
+void
+JoystickKeyboardController::set_joy_controls(Control id, bool value)
+{
+  if (jump_with_up_joy && id == Controller::UP)
+    controller->set_control(Controller::JUMP, value);
+
+  controller->set_control(id, value);
 }
 
 void
@@ -290,108 +343,21 @@ JoystickKeyboardController::process_event(const SDL_Event& event)
   switch(event.type) {
     case SDL_KEYUP:
     case SDL_KEYDOWN:
-      process_key_event(event);
+      process_key_event(event.key);
       break;
 
     case SDL_JOYAXISMOTION:
-      if (wait_for_joystick >= 0)
-        {
-          if (abs(event.jaxis.value) > dead_zone)
-            {
-              if (event.jaxis.value < 0)
-                reset_joyaxis(-(event.jaxis.axis + 1), Control(wait_for_joystick));
-              else
-                reset_joyaxis(event.jaxis.axis + 1, Control(wait_for_joystick));
-
-              joystick_options_menu->update();
-              wait_for_joystick = -1;
-            }
-        }
-      else
-        {
-          // Split the axis into left and right, so that both can be
-          // mapped seperatly (needed for jump/down vs up/down)
-          int axis = event.jaxis.axis + 1;
-          
-          AxisMap::iterator left  = joy_axis_map.find(-axis);
-          AxisMap::iterator right = joy_axis_map.find(axis);
-
-          if(left == joy_axis_map.end()) {
-            std::cout << "Unmapped joyaxis " << (int)event.jaxis.axis << " moved" << std::endl;
-          } else {         
-            if (event.jaxis.value < -dead_zone)
-              controls[left->second]  = true;
-            else if (event.jaxis.value > dead_zone)
-              controls[left->second]  = false;
-            else
-              controls[left->second]  = false;
-          }
-
-          if(right == joy_axis_map.end()) {
-            std::cout << "Unmapped joyaxis " << (int)event.jaxis.axis << " moved" << std::endl;
-          } else {
-            if (event.jaxis.value < -dead_zone)
-              controls[right->second] = false;            
-            else if (event.jaxis.value > dead_zone)
-              controls[right->second] = true;              
-            else
-              controls[right->second] = false;
-          }
-        }
+      process_axis_event(event.jaxis);
       break;
 
     case SDL_JOYHATMOTION:
-      if(!use_hat)
-        break;
-
-      if(event.jhat.value & SDL_HAT_UP) {
-        controls[UP]   = true;
-        controls[DOWN] = false;
-      }
-      if(event.jhat.value & SDL_HAT_DOWN) {
-        controls[UP]   = false;
-        controls[DOWN] = true;
-      }
-      if(event.jhat.value & SDL_HAT_LEFT) {
-        controls[LEFT]  = true;
-        controls[RIGHT] = false;
-      }
-      if(event.jhat.value & SDL_HAT_RIGHT) {
-        controls[LEFT]  = false;
-        controls[RIGHT] = true;
-      }
-      if(event.jhat.value == SDL_HAT_CENTERED) {
-        controls[UP]    = false;
-        controls[DOWN]  = false;
-        controls[LEFT]  = false;
-        controls[RIGHT] = false;
-      }
+      process_hat_event(event.jhat);
       break;
 
     case SDL_JOYBUTTONDOWN:
     case SDL_JOYBUTTONUP:
-      {
-        if(wait_for_joystick >= 0) {
-          if(event.type == SDL_JOYBUTTONUP)
-            return;
-
-          Control c = (Control) wait_for_joystick;
-          reset_joybutton(event.jbutton.button, c);
-          reset();
-          joystick_options_menu->update();
-          wait_for_joystick = -1;
-          return;
-        }
-
-        ButtonMap::iterator i = joy_button_map.find(event.jbutton.button);
-        if(i == joy_button_map.end()) {
-          log_debug << "Unmapped joybutton " << (int)event.jbutton.button << " pressed" << std::endl;
-          return;
-        }
-
-        controls[i->second] = (event.type == SDL_JOYBUTTONDOWN);
-        break;
-      }
+      process_button_event(event.jbutton);
+      break;
 
     default:
       break;
@@ -399,44 +365,165 @@ JoystickKeyboardController::process_event(const SDL_Event& event)
 }
 
 void
-JoystickKeyboardController::process_key_event(const SDL_Event& event)
+JoystickKeyboardController::process_button_event(const SDL_JoyButtonEvent& jbutton)
+{
+  if(wait_for_joystick >= 0) 
+  {
+    if(jbutton.state == SDL_PRESSED)
+    {
+      bind_joybutton(jbutton.which, jbutton.button, (Control)wait_for_joystick);
+      MenuStorage::get_joystick_options_menu()->update();
+      reset();
+      wait_for_joystick = -1;
+    }
+  } 
+  else 
+  {
+    ButtonMap::iterator i = joy_button_map.find(std::make_pair(jbutton.which, jbutton.button));
+    if(i == joy_button_map.end()) {
+      log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
+    } else {
+      set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
+    }
+  }
+}
+
+void
+JoystickKeyboardController::process_axis_event(const SDL_JoyAxisEvent& jaxis)
 {
-  KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
+  if (wait_for_joystick >= 0)
+  {
+    if (abs(jaxis.value) > dead_zone) {
+      if (jaxis.value < 0)
+        bind_joyaxis(jaxis.which, -(jaxis.axis + 1), Control(wait_for_joystick));
+      else
+        bind_joyaxis(jaxis.which, jaxis.axis + 1, Control(wait_for_joystick));
 
-  // if console key was pressed: toggle console
-  if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
-    if (event.type != SDL_KEYDOWN) return;
-    Console::instance->toggle();
-    return;
+      MenuStorage::get_joystick_options_menu()->update();
+      wait_for_joystick = -1;
+    }
   }
+  else
+  {
+    // Split the axis into left and right, so that both can be
+    // mapped separately (needed for jump/down vs up/down)
+    int axis = jaxis.axis + 1;
 
-  // if console is open: send key there
-  if (Console::instance->hasFocus()) {
-    process_console_key_event(event);
-    return;
+    AxisMap::iterator left  = joy_axis_map.find(std::make_pair(jaxis.which, -axis));
+    AxisMap::iterator right = joy_axis_map.find(std::make_pair(jaxis.which, axis));
+
+    if(left == joy_axis_map.end()) {
+      // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
+    } else {
+      if (jaxis.value < -dead_zone)
+        set_joy_controls(left->second,  true);
+      else
+        set_joy_controls(left->second, false);
+    }
+
+    if(right == joy_axis_map.end()) {
+      // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
+    } else {
+      if (jaxis.value > dead_zone)
+        set_joy_controls(right->second, true);
+      else
+        set_joy_controls(right->second, false);
+    }
   }
+}
 
-  // if menu mode: send key there
-  if (Menu::current()) {
-    process_menu_key_event(event);
-    return;
+void
+JoystickKeyboardController::process_hat_event(const SDL_JoyHatEvent& jhat)
+{
+  Uint8 changed = hat_state ^ jhat.value;
+
+  if (wait_for_joystick >= 0)
+  {
+    if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
+      bind_joyhat(jhat.which, SDL_HAT_UP, Control(wait_for_joystick));
+
+    if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
+      bind_joyhat(jhat.which, SDL_HAT_DOWN, Control(wait_for_joystick));
+
+    if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
+      bind_joyhat(jhat.which, SDL_HAT_LEFT, Control(wait_for_joystick));
+
+    if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
+      bind_joyhat(jhat.which, SDL_HAT_RIGHT, Control(wait_for_joystick));
+
+    MenuStorage::get_joystick_options_menu()->update();
+    wait_for_joystick = -1;
   }
+  else
+  {
+    if (changed & SDL_HAT_UP)
+    {
+      HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_UP));
+      if (it != joy_hat_map.end())
+        set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
+    }
 
-  // default action: update controls
-  if(key_mapping == keymap.end()) {
-    log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
-    return;
+    if (changed & SDL_HAT_DOWN)
+    {
+      HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_DOWN));
+      if (it != joy_hat_map.end())
+        set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
+    }
+
+    if (changed & SDL_HAT_LEFT)
+    {
+      HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_LEFT));
+      if (it != joy_hat_map.end())
+        set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
+    }
+
+    if (changed & SDL_HAT_RIGHT)
+    {
+      HatMap::iterator it = joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_RIGHT));
+      if (it != joy_hat_map.end())
+        set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
+    }
+  }
+
+  hat_state = jhat.value;
+}
+
+void
+JoystickKeyboardController::process_key_event(const SDL_KeyboardEvent& event)
+{
+  KeyMap::iterator key_mapping = keymap.find(event.keysym.sym);
+
+  // if console key was pressed: toggle console
+  if ((key_mapping != keymap.end()) && (key_mapping->second == Controller::CONSOLE)) {
+    if (event.type == SDL_KEYDOWN) 
+      Console::instance->toggle();
+  } else {
+    if (Console::instance->hasFocus()) {
+      // if console is open: send key there
+      process_console_key_event(event);
+    } else if (MenuManager::current()) {
+      // if menu mode: send key there
+      process_menu_key_event(event);
+    } else if(key_mapping == keymap.end()) {
+      // default action: update controls
+      //log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
+    } else {
+      Control control = key_mapping->second;
+      bool value = (event.type == SDL_KEYDOWN);
+      controller->set_control(control, value);
+      if (jump_with_up_kbd && control == Controller::UP){
+        controller->set_control(Controller::JUMP, value);
+      }
+    }
   }
-  Control control = key_mapping->second;
-  controls[control] = (event.type == SDL_KEYDOWN);
 }
 
 void
-JoystickKeyboardController::process_console_key_event(const SDL_Event& event)
+JoystickKeyboardController::process_console_key_event(const SDL_KeyboardEvent& event)
 {
   if (event.type != SDL_KEYDOWN) return;
 
-  switch (event.key.keysym.sym) {
+  switch (event.keysym.sym) {
     case SDLK_RETURN:
       Console::instance->enter();
       break;
@@ -471,35 +558,35 @@ JoystickKeyboardController::process_console_key_event(const SDL_Event& event)
       Console::instance->move_cursor(+1);
       break;
     default:
-      int c = event.key.keysym.unicode;
+      int c = event.keysym.unicode;
       if ((c >= 32) && (c <= 126)) {
-       Console::instance->input((char)c);
+        Console::instance->input((char)c);
       }
       break;
   }
 }
 
 void
-JoystickKeyboardController::process_menu_key_event(const SDL_Event& event)
+JoystickKeyboardController::process_menu_key_event(const SDL_KeyboardEvent& event)
 {
   // wait for key mode?
   if(wait_for_key >= 0) {
     if(event.type == SDL_KEYUP)
       return;
 
-    if(event.key.keysym.sym != SDLK_ESCAPE
-        && event.key.keysym.sym != SDLK_PAUSE) {
-      reset_key(event.key.keysym.sym, (Control) wait_for_key);
+    if(event.keysym.sym != SDLK_ESCAPE
+       && event.keysym.sym != SDLK_PAUSE) {
+      bind_key(event.keysym.sym, Control(wait_for_key));
     }
     reset();
-    key_options_menu->update();
+    MenuStorage::get_key_options_menu()->update();
     wait_for_key = -1;
     return;
   }
   if(wait_for_joystick >= 0) {
-    if(event.key.keysym.sym == SDLK_ESCAPE) {
+    if(event.keysym.sym == SDLK_ESCAPE) {
       reset();
-      joystick_options_menu->update();
+      MenuStorage::get_joystick_options_menu()->update();
       wait_for_joystick = -1;
     }
     return;
@@ -509,122 +596,96 @@ JoystickKeyboardController::process_menu_key_event(const SDL_Event& event)
   /* we use default keys when the menu is open (to avoid problems when
    * redefining keys to invalid settings
    */
-  switch(event.key.keysym.sym) {
+  switch(event.keysym.sym) {
     case SDLK_UP:
-      control = UP;
+      control = Controller::UP;
       break;
     case SDLK_DOWN:
-      control = DOWN;
+      control = Controller::DOWN;
       break;
     case SDLK_LEFT:
-      control = LEFT;
+      control = Controller::LEFT;
       break;
     case SDLK_RIGHT:
-      control = RIGHT;
+      control = Controller::RIGHT;
       break;
     case SDLK_SPACE:
     case SDLK_RETURN:
     case SDLK_KP_ENTER:
-      control = MENU_SELECT;
+      control = Controller::MENU_SELECT;
       break;
     case SDLK_ESCAPE:
     case SDLK_PAUSE:
-      control = PAUSE_MENU;
+      control = Controller::PAUSE_MENU;
       break;
     default:
       return;
       break;
   }
 
-  controls[control] = (event.type == SDL_KEYDOWN);
+  controller->set_control(control, (event.type == SDL_KEYDOWN));
 }
 
 void
-JoystickKeyboardController::reset_joyaxis(int axis, Control control)
+JoystickKeyboardController::unbind_joystick_control(Control control)
 {
-  // axis isn't the SDL axis number, but axisnumber + 1 with sign
-  // changed depending on if the positive or negative end is to be
-  // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
-  // etc.)
-  
   // remove all previous mappings for that control
-  for(AxisMap::iterator i = joy_axis_map.begin(); 
-      i != joy_axis_map.end(); /* no ++i */) {
-    if(i->second == control) {
-      AxisMap::iterator e = i;
-      ++i;
-      joy_axis_map.erase(e);
-    } else {
+  for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); /* no ++i */) {
+    if(i->second == control)
+      joy_axis_map.erase(i++);
+    else
       ++i;
-    }
   }
 
-  // remove all previous mappings for that control
-  for(ButtonMap::iterator i = joy_button_map.begin();
-      i != joy_button_map.end(); /* no ++i */) {
-    if(i->second == control) {
-      ButtonMap::iterator e = i;
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); /* no ++i */) {
+    if(i->second == control)
+      joy_button_map.erase(i++);
+    else
       ++i;
-      joy_button_map.erase(e);
-    } else {
+  }
+
+  for(HatMap::iterator i = joy_hat_map.begin();  i != joy_hat_map.end(); /* no ++i */) {
+    if(i->second == control)
+      joy_hat_map.erase(i++);
+    else
       ++i;
-    }
   }
+}
+
+void
+JoystickKeyboardController::bind_joyaxis(JoyId joy_id, int axis, Control control)
+{
+  // axis isn't the SDL axis number, but axisnumber + 1 with sign
+  // changed depending on if the positive or negative end is to be
+  // used (negative axis 0 becomes -1, positive axis 2 becomes +3,
+  // etc.)
 
-  // remove all previous and for that axis
-  AxisMap::iterator i = joy_axis_map.find(axis);
-  if(i != joy_axis_map.end())
-    joy_axis_map.erase(i);
+  unbind_joystick_control(control);
 
   // add new mapping
-  joy_axis_map.insert(std::make_pair(axis, control));
+  joy_axis_map[std::make_pair(joy_id, axis)] = control;
 }
 
 void
-JoystickKeyboardController::reset_joybutton(int button, Control control)
+JoystickKeyboardController::bind_joyhat(JoyId joy_id, int dir, Control c)
 {
-  // remove all previous mappings for that control
-  for(AxisMap::iterator i = joy_axis_map.begin(); 
-      i != joy_axis_map.end(); /* no ++i */) {
-    if(i->second == control) {
-      AxisMap::iterator e = i;
-      ++i;
-      joy_axis_map.erase(e);
-    } else {
-      ++i;
-    }
-  }
-
-  // remove all previous mappings for that control and for that key
-  for(ButtonMap::iterator i = joy_button_map.begin();
-      i != joy_button_map.end(); /* no ++i */) {
-    if(i->second == control) {
-      ButtonMap::iterator e = i;
-      ++i;
-      joy_button_map.erase(e);
-    } else {
-      ++i;
-    }
-  }
-  ButtonMap::iterator i = joy_button_map.find(button);
-  if(i != joy_button_map.end())
-    joy_button_map.erase(i);
+  unbind_joystick_control(c);
 
   // add new mapping
-  joy_button_map.insert(std::make_pair(button, control));
+  joy_hat_map[std::make_pair(joy_id, dir)] = c;
+}
 
-  // map all unused buttons to MENU_SELECT
-  for(int b = 0; b < max_joybuttons; ++b) {
-    ButtonMap::iterator i = joy_button_map.find(b);
-    if(i != joy_button_map.end())
-      continue;
+void
+JoystickKeyboardController::bind_joybutton(JoyId joy_id, int button, Control control)
+{
+  unbind_joystick_control(control);
 
-    joy_button_map.insert(std::make_pair(b, MENU_SELECT));
-  }
+  // add new mapping
+  joy_button_map[std::make_pair(joy_id, button)] = control;
 }
 
 void
-JoystickKeyboardController::reset_key(SDLKey key, Control control)
+JoystickKeyboardController::bind_key(SDLKey key, Control control)
 {
   // remove all previous mappings for that control and for that key
   for(KeyMap::iterator i = keymap.begin();
@@ -637,12 +698,32 @@ JoystickKeyboardController::reset_key(SDLKey key, Control control)
       ++i;
     }
   }
+
   KeyMap::iterator i = keymap.find(key);
   if(i != keymap.end())
     keymap.erase(i);
 
   // add new mapping
-  keymap.insert(std::make_pair(key, control));
+  keymap[key] = control;
+}
+
+void
+JoystickKeyboardController::print_joystick_mappings()
+{
+  std::cout << "Joystick Mappings" << std::endl;
+  std::cout << "-----------------" << std::endl;
+  for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
+    std::cout << "Axis: " << i->first.second << " -> " << i->second << std::endl;
+  }
+
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
+    std::cout << "Button: " << i->first.second << " -> " << i->second << std::endl;
+  }
+
+  for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+    std::cout << "Hat: " << i->first.second << " -> " << i->second << std::endl;
+  }
+  std::cout << std::endl;
 }
 
 SDLKey
@@ -661,237 +742,32 @@ JoystickKeyboardController::reversemap_joyaxis(Control c)
 {
   for(AxisMap::iterator i = joy_axis_map.begin(); i != joy_axis_map.end(); ++i) {
     if(i->second == c)
-      return i->first;
+      return i->first.second;
   }
-  
+
   return 0;
 }
 
 int
 JoystickKeyboardController::reversemap_joybutton(Control c)
 {
-  for(ButtonMap::iterator i = joy_button_map.begin();
-      i != joy_button_map.end(); ++i) {
+  for(ButtonMap::iterator i = joy_button_map.begin(); i != joy_button_map.end(); ++i) {
     if(i->second == c)
-      return i->first;
+      return i->first.second;
   }
 
   return -1;
 }
 
-Menu*
-JoystickKeyboardController::get_key_options_menu()
-{
-  if(key_options_menu == 0) {
-    key_options_menu = new KeyboardMenu(this);
-  }
-
-  return key_options_menu;
-}
-
-Menu*
-JoystickKeyboardController::get_joystick_options_menu()
-{
-  if(joystick_options_menu == 0) {
-    joystick_options_menu = new JoystickMenu(this);
-  }
-
-  return joystick_options_menu;
-}
-
-//----------------------------------------------------------------------------
-
-JoystickKeyboardController::KeyboardMenu::KeyboardMenu(
-    JoystickKeyboardController* _controller)
-  : controller(_controller)
-{
-    add_label(_("Setup Keyboard"));
-    add_hl();
-    add_controlfield(Controller::UP,         _("Up"));
-    add_controlfield(Controller::DOWN,       _("Down"));
-    add_controlfield(Controller::LEFT,       _("Left"));
-    add_controlfield(Controller::RIGHT,      _("Right"));
-    add_controlfield(Controller::JUMP,       _("Jump"));
-    add_controlfield(Controller::ACTION,     _("Action"));
-    add_controlfield(Controller::PEEK_LEFT,  _("Peek Left"));
-    add_controlfield(Controller::PEEK_RIGHT, _("Peek Right"));
-    if (config->console_enabled) {
-      add_controlfield(Controller::CONSOLE, _("Console"));
-    }
-    add_hl();
-    add_back(_("Back"));
-    update();
-}
-
-JoystickKeyboardController::KeyboardMenu::~KeyboardMenu()
-{}
-
-std::string
-JoystickKeyboardController::KeyboardMenu::get_key_name(SDLKey key)
-{
-  switch(key) {
-    case SDLK_UNKNOWN:
-      return _("None");
-    case SDLK_UP:
-      return _("Up cursor");
-    case SDLK_DOWN:
-      return _("Down cursor");
-    case SDLK_LEFT:
-      return _("Left cursor");
-    case SDLK_RIGHT:
-      return _("Right cursor");
-    case SDLK_RETURN:
-      return _("Return");
-    case SDLK_SPACE:
-      return _("Space");
-    case SDLK_RSHIFT:
-      return _("Right Shift");
-    case SDLK_LSHIFT:
-      return _("Left Shift");
-    case SDLK_RCTRL:
-      return _("Right Control");
-    case SDLK_LCTRL:
-      return _("Left Control");
-    case SDLK_RALT:
-      return _("Right Alt");
-    case SDLK_LALT:
-      return _("Left Alt");
-    default:
-      return SDL_GetKeyName((SDLKey) key);
-  }
-}
-
-void
-JoystickKeyboardController::KeyboardMenu::menu_action(MenuItem* item)
-{
-  assert(item->id >= 0 && item->id < Controller::CONTROLCOUNT);
-  item->change_input(_("Press Key"));
-  controller->wait_for_key = item->id;
-}
-
-void
-JoystickKeyboardController::KeyboardMenu::update()
-{
-  // update menu
-  get_item_by_id((int) Controller::UP).change_input(get_key_name(
-    controller->reversemap_key(Controller::UP)));
-  get_item_by_id((int) Controller::DOWN).change_input(get_key_name(
-    controller->reversemap_key(Controller::DOWN)));
-  get_item_by_id((int) Controller::LEFT).change_input(get_key_name(
-    controller->reversemap_key(Controller::LEFT)));
-  get_item_by_id((int) Controller::RIGHT).change_input(get_key_name(
-    controller->reversemap_key(Controller::RIGHT)));
-  get_item_by_id((int) Controller::JUMP).change_input(get_key_name(
-    controller->reversemap_key(Controller::JUMP)));
-  get_item_by_id((int) Controller::ACTION).change_input(get_key_name(
-    controller->reversemap_key(Controller::ACTION)));
-  get_item_by_id((int) Controller::PEEK_LEFT).change_input(get_key_name(
-    controller->reversemap_key(Controller::PEEK_LEFT)));
-  get_item_by_id((int) Controller::PEEK_RIGHT).change_input(get_key_name(
-    controller->reversemap_key(Controller::PEEK_RIGHT)));
-  if (config->console_enabled) {
-    get_item_by_id((int) Controller::CONSOLE).change_input(get_key_name(
-      controller->reversemap_key(Controller::CONSOLE)));
-  }
-}
-
-//---------------------------------------------------------------------------
-
-JoystickKeyboardController::JoystickMenu::JoystickMenu(
-  JoystickKeyboardController* _controller)
-  : controller(_controller)
+int
+JoystickKeyboardController::reversemap_joyhat(Control c)
 {
-  add_label(_("Setup Joystick"));
-  add_hl();
-  if(controller->joysticks.size() > 0) {
-    add_controlfield(Controller::UP,          _("Up"));
-    add_controlfield(Controller::DOWN,        _("Down"));
-    add_controlfield(Controller::LEFT,        _("Left"));
-    add_controlfield(Controller::RIGHT,       _("Right"));
-    add_controlfield(Controller::JUMP,        _("Jump"));
-    add_controlfield(Controller::ACTION,      _("Action"));
-    add_controlfield(Controller::PAUSE_MENU,  _("Pause/Menu"));
-    add_controlfield(Controller::PEEK_LEFT,   _("Peek Left"));
-    add_controlfield(Controller::PEEK_RIGHT,  _("Peek Right"));
-  } else {
-    add_deactive(-1, _("No Joysticks found"));
+  for(HatMap::iterator i = joy_hat_map.begin(); i != joy_hat_map.end(); ++i) {
+    if(i->second == c)
+      return i->first.second;
   }
-  add_hl();
-  add_back(_("Back"));
-  update();
-}
-
-JoystickKeyboardController::JoystickMenu::~JoystickMenu()
-{}
-
-std::string
-JoystickKeyboardController::JoystickMenu::get_button_name(int button)
-{
-  if(button < 0)
-    return _("None");
-
-  std::ostringstream name;
-  name << "Button " << button;
-  return name.str();
-}
-
-void
-JoystickKeyboardController::JoystickMenu::menu_action(MenuItem* item)
-{
-  assert(item->id >= 0 && item->id < Controller::CONTROLCOUNT);
-  item->change_input(_("Press Button"));
-  controller->wait_for_joystick = item->id;
-}
 
-void
-JoystickKeyboardController::JoystickMenu::update_menu_item(Control id)
-{
-  int button = controller->reversemap_joybutton(id);
-  int axis   = controller->reversemap_joyaxis(id);
-  
-  if (button != -1) {
-    get_item_by_id((int)id).change_input(get_button_name(button));
-  } else if (axis != 0) {
-    std::ostringstream name;
-
-    name << "Axis ";
-
-    if (axis < 0)
-      name << "-";
-    else
-      name << "+";
-
-    if (abs(axis) == 1)
-      name << "X";
-    else if (abs(axis) == 2)
-      name << "Y";
-    else if (abs(axis) == 2)
-      name << "X2";
-    else if (abs(axis) == 3)
-      name << "Y2";
-    else 
-      name << abs(axis);
-
-    get_item_by_id((int)id).change_input(name.str());
-  } else {
-    get_item_by_id((int)id).change_input("None");
-  }
+  return -1;
 }
 
-void
-JoystickKeyboardController::JoystickMenu::update()
-{
-  if(controller->joysticks.size() == 0)
-    return;
-
-  update_menu_item(Controller::UP);
-  update_menu_item(Controller::DOWN);
-  update_menu_item(Controller::LEFT);
-  update_menu_item(Controller::RIGHT);
-
-  update_menu_item(Controller::JUMP);
-  update_menu_item(Controller::ACTION);
-  update_menu_item(Controller::PAUSE_MENU);
-  update_menu_item(Controller::PEEK_LEFT);
-  update_menu_item(Controller::PEEK_RIGHT);
-}
+/* EOF */