void OptionsScreenInput2::gotSensedInput(const Input& sensed_input)
{
    const bool keyboard = (m_config->isKeyboard() &&
                           sensed_input.m_type == Input::IT_KEYBOARD);
    const bool gamepad =  (sensed_input.m_type == Input::IT_STICKMOTION ||
                           sensed_input.m_type == Input::IT_STICKBUTTON) &&
                           m_config->isGamePad();

    if (keyboard)
    {
        if (UserConfigParams::logMisc())
        {
            Log::info("OptionsScreenInput2", "Binding %s: setting to keyboard key %d",
                KartActionStrings[binding_to_set].c_str(), sensed_input.m_button_id);
        }

        KeyboardConfig* keyboard = (KeyboardConfig*)m_config;
        keyboard->setBinding(binding_to_set, Input::IT_KEYBOARD,
                             sensed_input.m_button_id, Input::AD_NEUTRAL,
                             Input::AR_HALF,
                             sensed_input.m_character);

        // refresh display
        updateInputButtons();
    }
    else if (gamepad)
    {
        if (UserConfigParams::logMisc())
        {
            Log::info("OptionsScreenInput2", "Binding %s: setting to gamepad #%d",
                KartActionStrings[binding_to_set].c_str(), sensed_input.m_device_id);

            if (sensed_input.m_type == Input::IT_STICKMOTION)
            {
                Log::info("OptionsScreenInput2", "Axis %d; direction %s", sensed_input.m_button_id,
                    sensed_input.m_axis_direction == Input::AD_NEGATIVE ? "-" : "+");
            }
            else if (sensed_input.m_type == Input::IT_STICKBUTTON)
            {
                Log::info("OptionsScreenInput2", "Button %d", sensed_input.m_button_id);
            }
            else
            {
                Log::info("OptionsScreenInput2", "Sensed unknown gamepad event type??");
            }
        }

        GamePadDevice *gpad = input_manager->getDeviceManager()
                            ->getGamePadFromIrrID(sensed_input.m_device_id);

        std::string gamepad_name = gpad ? gpad->getName() : "UNKNOWN DEVICE";
        if (m_config->getName() == gamepad_name)
        {
            GamepadConfig* config =  (GamepadConfig*)m_config;
            config->setBinding(binding_to_set, sensed_input.m_type,
                               sensed_input.m_button_id,
                              (Input::AxisDirection)sensed_input.m_axis_direction,
                              (Input::AxisRange)sensed_input.m_axis_range);

            // refresh display
            updateInputButtons();
        }
    }
    else if (sensed_input.m_type == Input::IT_NONE)
    {
        if (UserConfigParams::logMisc())
        {
            Log::info("OptionsScreenInput2", "Binding %s: setting to keyboard key NONE",
                KartActionStrings[binding_to_set].c_str());
        }

        KeyboardConfig* keyboard = (KeyboardConfig*)m_config;
        keyboard->setBinding(binding_to_set, Input::IT_NONE,
                             sensed_input.m_button_id, Input::AD_NEUTRAL,
                             Input::AR_HALF,
                             sensed_input.m_character);

        // refresh display
        updateInputButtons();
    }
    else
    {
        return;
    }

    ModalDialog::dismiss();
    input_manager->setMode(InputManager::MENU);

    if (keyboard && (sensed_input.m_button_id == irr::KEY_SHIFT ||
                     sensed_input.m_button_id == irr::KEY_LSHIFT ||
                     sensed_input.m_button_id == irr::KEY_RSHIFT))
    {
        new MessageDialog(_("Warning, 'Shift' is not a recommended key : when "
                            "shift is pressed down, all keys that contain a "
                            "character that is different in upper-case will "
                            "stop working."));
    }

    // re-select the previous button (TODO!)
    //ButtonWidget* btn =
    //    getWidget<ButtonWidget>(binding_to_set_button.c_str());
    //if(btn != NULL) btn->setFocusForPlayer(PLAYER_ID_GAME_MASTER);

    // save new binding to file
    input_manager->getDeviceManager()->save();
}   // gotSensedInput
/** Handles the conversion from some input to a GameAction and its distribution
 * to the currently active menu.
 * It also handles whether the game is currently sensing input. It does so by
 * suppressing the distribution of the input as a GameAction. Instead the
 * input is stored in 'm_sensed_input' and GA_SENSE_COMPLETE is distributed. If
 * however the input in question has resolved to GA_LEAVE this is treated as
 * an attempt of the user to cancel the sensing. In that case GA_SENSE_CANCEL
 * is distributed.
 *
 * Note: It is the obligation of the called menu to switch of the sense mode.
 *
 */
void InputManager::dispatchInput(Input::InputType type, int deviceID,
                                 int button,
                                 Input::AxisDirection axisDirection, int value,
                                 bool shift_mask)
{
    // Act different in input sensing mode.
    if (m_mode == INPUT_SENSE_KEYBOARD ||
        m_mode == INPUT_SENSE_GAMEPAD)
    {
        // Do not pick disabled gamepads for input sensing
         if (type == Input::IT_STICKBUTTON || type == Input::IT_STICKMOTION)
        {
             GamePadDevice *gPad = m_device_manager->getGamePadFromIrrID(deviceID);
             // This can happen in case of automatically ignored accelerator
             // devices, which are not part of stk's gamepad mapping.
             if (!gPad) return;
             DeviceConfig *conf = gPad->getConfiguration();
             if (!conf->isEnabled())
                 return;
         }

        inputSensing(type, deviceID, button, axisDirection,  value);
        return;
    }

    // Abort demo mode if a key is pressed during the race in demo mode
    if(dynamic_cast<DemoWorld*>(World::getWorld()))
    {
        race_manager->exitRace();
        StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
        return;
    }

    StateManager::ActivePlayer*   player = NULL;
    PlayerAction    action;
    bool action_found = m_device_manager->translateInput(type, deviceID,
                                                         button, axisDirection,
                                                         &value, m_mode,
                                                         &player, &action);

    // in menus, some keyboard keys are standard (before each player selected
    // his device). So if a key could not be mapped to any known binding,
    // fall back to check the defaults.
    if (!action_found &&
            StateManager::get()->getGameState() != GUIEngine::GAME &&
            type == Input::IT_KEYBOARD &&
            m_mode == MENU && m_device_manager->getAssignMode() == NO_ASSIGN)
    {
        action = PA_BEFORE_FIRST;

        if      (button == KEY_UP)     action = PA_MENU_UP;
        else if (button == KEY_DOWN)   action = PA_MENU_DOWN;
        else if (button == KEY_LEFT)   action = PA_MENU_LEFT;
        else if (button == KEY_RIGHT)  action = PA_MENU_RIGHT;
        else if (button == KEY_SPACE)  action = PA_MENU_SELECT;
        else if (button == KEY_RETURN) action = PA_MENU_SELECT;
        else if (button == KEY_TAB)    
        {
            if (shift_mask)
            {
                action = PA_MENU_UP;
            }
            else
            {
                action = PA_MENU_DOWN;
            }
        }

        if (button == KEY_RETURN && GUIEngine::ModalDialog::isADialogActive())
        {
            GUIEngine::ModalDialog::onEnterPressed();
        }

        if (action != PA_BEFORE_FIRST)
        {
            action_found = true;
            player = NULL;
        }
    }

    // do something with the key if it matches a binding
    if (action_found)
    {
        // If we're in the kart menu awaiting new players, do special things
        // when a device presses fire or rescue
        if (m_device_manager->getAssignMode() == DETECT_NEW)
        {
            // Player is unjoining
            if ((player != NULL) && (action == PA_RESCUE ||
                                     action == PA_MENU_CANCEL ) )
            {
                // returns true if the event was handled
                if (KartSelectionScreen::getRunningInstance()->playerQuit( player ))
                {
                    return; // we're done here
                }
            }

            /* The way this is currently structured, any time an event is
               received from an input device that is not associated with a
               player and the device manager is in DETECT_NEW mode, the event
               is ignored, unless it is a PA_FIRE event (a player is joining)

               perhaps it will be good to let unassigned devices back out
               of the kart selection menu?
            */

            else if (player == NULL)
            {
                // New player is joining
                if (action == PA_FIRE || action == PA_MENU_SELECT)
                {
                    InputDevice *device = NULL;
                    if (type == Input::IT_KEYBOARD)
                    {
                        //Log::info("InputManager", "New Player Joining with Key %d", button);
                        device = m_device_manager->getKeyboardFromBtnID(button);
                    }
                    else if (type == Input::IT_STICKBUTTON ||
                             type == Input::IT_STICKMOTION    )
                    {
                        device = m_device_manager->getGamePadFromIrrID(deviceID);
                    }

                    if (device != NULL)
                    {
                        KartSelectionScreen::getRunningInstance()->joinPlayer(device);
                    }
                }
                return; // we're done here, ignore devices that aren't
                        // associated with players
            }
        }

        // ... when in-game
        if (StateManager::get()->getGameState() == GUIEngine::GAME &&
             !GUIEngine::ModalDialog::isADialogActive()            &&
             !race_manager->isWatchingReplay() )
        {
            if (player == NULL)
            {
                // Prevent null pointer crash
                return;
            }

            // Find the corresponding PlayerKart from our ActivePlayer instance
            AbstractKart* pk = player->getKart();

            if (pk == NULL)
            {
                Log::error("InputManager::dispatchInput", "Trying to process "
                    "action for an unknown player");
                return;
            }

            Controller* controller = pk->getController();
            if (controller != NULL) controller->action(action, abs(value));
        }
        else if (race_manager->isWatchingReplay())
        {
            // Get the first ghost kart
            World::getWorld()->getKart(0)
                ->getController()->action(action, abs(value));
        }
        // ... when in menus
        else
        {
            // reset timer when released
            if (abs(value) == 0 &&  type == Input::IT_STICKBUTTON)
            {
                m_timer_in_use = false;
                m_timer = 0;
            }

            // When in master-only mode, we can safely assume that players
            // are set up, contrarly to early menus where we accept every
            // input because players are not set-up yet
            if (m_master_player_only && player == NULL)
            {
                if (type == Input::IT_STICKMOTION ||
                    type == Input::IT_STICKBUTTON)
                {
                    GamePadDevice* gp =
                        getDeviceManager()->getGamePadFromIrrID(deviceID);

                    // Check for deadzone
                    if (gp != NULL && gp->moved(value))
                    {
                        //I18N: message shown when an input device is used but
                        // is not associated to any player
                        GUIEngine::showMessage(
                            _("Ignoring '%s'. You needed to join earlier to play!",
                            core::stringw(gp->getName().c_str())));
                    }
                }
                return;
            }

            // menu input
            if (!m_timer_in_use)
            {
                if (abs(value) > Input::MAX_VALUE*2/3)
                {
                    m_timer_in_use = true;
                    m_timer = 0.25;
                }

                // player may be NULL in early menus, before player setup has
                // been performed
                int playerID = (player == NULL ? 0 : player->getID());

                // If only the master player can act, and this player is not
                // the master, ignore his input
                if (m_device_manager->getAssignMode() == ASSIGN &&
                    m_master_player_only &&
                    playerID != PLAYER_ID_GAME_MASTER)
                {
                    //I18N: message shown when a player that isn't game master
                    //I18N: tries to modify options that only the game master
                    //I18N: is allowed to
                    GUIEngine::showMessage(
                        _("Only the Game Master may act at this point!"));
                    return;
                }

                // all is good, pass the translated input event on to the
                // event handler
                GUIEngine::EventHandler::get()
                    ->processGUIAction(action, deviceID, abs(value), type,
                                       playerID);
            }
        }
    }
    else if (type == Input::IT_KEYBOARD)
    {
        // keyboard press not handled by device manager / bindings.
        // Check static bindings...
        handleStaticAction( button, value );
    }
}   // input