Example #1
0
/**
 * Called on keyboard events [indirectly] by irrLicht
 *
 * Analog axes can have any value from [-32768, 32767].
 *
 * There are no negative values. Instead this is reported as an axis with a
 * negative direction. This simplifies input configuration and allows greater
 * flexibility (= treat 4 directions as four buttons).
 *
 * Returns whether to halt the event's propagation here
 */
EventPropagation InputManager::input(const SEvent& event)
{
    if (event.EventType == EET_JOYSTICK_INPUT_EVENT)
    {
        // Axes - FIXME, instead of checking all of them, ask the bindings 
        // which ones to poll
        for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ; 
              axis_id++)
        {
            int value = event.JoystickEvent.Axis[axis_id];

            if (UserConfigParams::m_gamepad_debug)
            {
                printf("axis motion: gamepad_id=%d axis=%d value=%d\n",
                       event.JoystickEvent.Joystick, axis_id, value);
            }

            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, 
                          axis_id, Input::AD_NEUTRAL, value);
        }
        
        if (event.JoystickEvent.POV == 65535)
        {
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_H_ID, Input::AD_NEUTRAL, 0);
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_V_ID, Input::AD_NEUTRAL, 0);
        }
        else
        {
            // *0.017453925f is to convert degrees to radians
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, 
                          Input::HAT_H_ID, Input::AD_NEUTRAL,
                          (int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f)
                                *Input::MAX_VALUE));
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_V_ID, Input::AD_NEUTRAL,
                          (int)(sin(event.JoystickEvent.POV*0.017453925f/100.0f)
                                *Input::MAX_VALUE));
        }
        
        GamePadDevice* gp = 
            getDeviceList()->getGamePadFromIrrID(event.JoystickEvent.Joystick);

        if (gp == NULL)
        {
            // Prevent null pointer crash
            return EVENT_BLOCK;
        }

        for(int i=0; i<gp->m_button_count; i++)
        {
            const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i);
            
            // Only report button events when the state of the button changes
            if ((!gp->isButtonPressed(i) &&  isButtonPressed) || 
                 (gp->isButtonPressed(i) && !isButtonPressed)    )
            {
                if (UserConfigParams::m_gamepad_debug)
                {
                    printf("button %i, status=%i\n", i, isButtonPressed);
                }
                
                dispatchInput(Input::IT_STICKBUTTON, 
                              event.JoystickEvent.Joystick, i, 
                              Input::AD_POSITIVE,
                              isButtonPressed ? Input::MAX_VALUE : 0);
            }
            gp->setButtonPressed(i, isButtonPressed);
        }

    }
    else if (event.EventType == EET_KEY_INPUT_EVENT)
    {
        // On some systems (linux esp.) certain keys (e.g. [] ) have a 0 
        // Key value, but do have a value defined in the Char field.
        // So to distinguish them (otherwise [] would both be mapped to
        // the same value 0, which means we can't distinguish which key
        // was actually pressed anymore). We set bit 10 which should
        // allow us to distinguish those artifical keys from the
        // 'real' keys.
        const int key = event.KeyInput.Key ? event.KeyInput.Key
                                           : event.KeyInput.Char+1024;

        if (event.KeyInput.PressedDown)
        {
            // escape is a little special
            if (key == KEY_ESCAPE)
            {
                StateManager::get()->escapePressed();
                return EVENT_BLOCK;
            }
            // 'backspace' in a text control must never be mapped, since user
            // can be in a text area trying to erase text (and if it's mapped 
            // to rescue that would dismiss the dialog instead of erasing a
            // single letter). Same for spacebar. Same for letters.
            if (GUIEngine::isWithinATextBox())
            {
                if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT)
                {
                    return EVENT_LET;
                }
                if (key >= KEY_KEY_0 && key <= KEY_KEY_Z)
                {
                    return EVENT_LET;
                }
            }
            
            const bool wasInTextBox = GUIEngine::isWithinATextBox();
            
            dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key,
                          Input::AD_POSITIVE, Input::MAX_VALUE);
            
            // if this action took us into a text box, don't let event continue
            // (FIXME not the cleanest solution)
            if (!wasInTextBox && GUIEngine::isWithinATextBox())
            {
                return EVENT_BLOCK;
            }

        }
        else
        {
            // 'backspace' in a text control must never be mapped, since user
            // can be in a text area trying to erase text (and if it's mapped 
            // to rescue that would dismiss the dialog instead of erasing a 
            // single letter). Same for spacebar. Same for letters.
            if (GUIEngine::isWithinATextBox())
            {
                if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT)
                {
                    return EVENT_LET;
                }
                if (key >= KEY_KEY_0 && key <= KEY_KEY_Z)
                {
                    return EVENT_LET;
                }
            }
            
            dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key, 
                          Input::AD_POSITIVE, 0);
            return EVENT_BLOCK; // Don't propagate key up events
        }
    }
#if 0 // in case we ever use mouse in-game...
    else if(event.EventType == EET_MOUSE_INPUT_EVENT)
    {
        const int type = event.MouseInput.Event;

        if(type == EMIE_MOUSE_MOVED)
        {
            // m_mouse_x = event.MouseInput.X;
            // m_mouse_y = event.MouseInput.Y;
            //const int wheel = event.MouseInput.Wheel;
        }

        /*
        EMIE_LMOUSE_PRESSED_DOWN    Left mouse button was pressed down.
        EMIE_RMOUSE_PRESSED_DOWN    Right mouse button was pressed down.
        EMIE_MMOUSE_PRESSED_DOWN    Middle mouse button was pressed down.
        EMIE_LMOUSE_LEFT_UP     Left mouse button was left up.
        EMIE_RMOUSE_LEFT_UP     Right mouse button was left up.
        EMIE_MMOUSE_LEFT_UP     Middle mouse button was left up.
        EMIE_MOUSE_MOVED    The mouse cursor changed its position.
        EMIE_MOUSE_WHEEL    The mouse wheel was moved. Use Wheel value in 
                            event data to find out in what direction and 
                            how fast.
         */
    }
#endif
    
    // block events in all modes but initial menus (except in text boxes to 
    // allow typing, and except in modal dialogs in-game)
    // FIXME: 1) that's awful logic 2) that's not what the code below does, 
    // events are never blocked in menus
    if (getDeviceList()->getAssignMode() != NO_ASSIGN && 
        !GUIEngine::isWithinATextBox() &&
        (!GUIEngine::ModalDialog::isADialogActive() && 
        StateManager::get()->getGameState() == GUIEngine::GAME))
    {
        return EVENT_BLOCK;
    }
    else
    {
        return EVENT_LET;
    }
}
/**
 * Called on keyboard events [indirectly] by irrLicht
 *
 * Analog axes can have any value from [-32768, 32767].
 *
 * There are no negative values. Instead this is reported as an axis with a
 * negative direction. This simplifies input configuration and allows greater
 * flexibility (= treat 4 directions as four buttons).
 *
 * Returns whether to halt the event's propagation here
 */
EventPropagation InputManager::input(const SEvent& event)
{
    if (event.EventType == EET_JOYSTICK_INPUT_EVENT)
    {
        // Axes - FIXME, instead of checking all of them, ask the bindings
        // which ones to poll
        for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ;
              axis_id++)
        {
            int value = event.JoystickEvent.Axis[axis_id];

            if (UserConfigParams::m_gamepad_debug)
            {
                Log::info("InputManager",
                          "axis motion: gamepad_id=%d axis=%d value=%d",
                          event.JoystickEvent.Joystick, axis_id, value);
            }

            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          axis_id, Input::AD_NEUTRAL, value);
        }

        if (event.JoystickEvent.POV == 65535)
        {
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_H_ID, Input::AD_NEUTRAL, 0);
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_V_ID, Input::AD_NEUTRAL, 0);
        }
        else
        {
            // *0.017453925f is to convert degrees to radians
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_H_ID, Input::AD_NEUTRAL,
                          (int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f)
                                *Input::MAX_VALUE));
            dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
                          Input::HAT_V_ID, Input::AD_NEUTRAL,
                          (int)(sin(event.JoystickEvent.POV*0.017453925f/100.0f)
                                *Input::MAX_VALUE));
        }

        GamePadDevice* gp =
            getDeviceManager()->getGamePadFromIrrID(event.JoystickEvent.Joystick);

        if (gp == NULL)
        {
            // Prevent null pointer crash
            return EVENT_BLOCK;
        }

        for(int i=0; i<gp->getNumberOfButtons(); i++)
        {
            const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i);

            // Only report button events when the state of the button changes
            if ((!gp->isButtonPressed(i) &&  isButtonPressed) ||
                 (gp->isButtonPressed(i) && !isButtonPressed)    )
            {
                if (UserConfigParams::m_gamepad_debug)
                {
                    Log::info("InputManager", "button %i, status=%i",
                              i, isButtonPressed);
                }

                dispatchInput(Input::IT_STICKBUTTON,
                              event.JoystickEvent.Joystick, i,
                              Input::AD_POSITIVE,
                              isButtonPressed ? Input::MAX_VALUE : 0);
            }
            gp->setButtonPressed(i, isButtonPressed);
        }

    }
    else if (event.EventType == EET_KEY_INPUT_EVENT)
    {
        // On some systems (linux esp.) certain keys (e.g. [] ) have a 0
        // Key value, but do have a value defined in the Char field.
        // So to distinguish them (otherwise [] would both be mapped to
        // the same value 0, which means we can't distinguish which key
        // was actually pressed anymore), we set bit 10 which should
        // allow us to distinguish those artifical keys from the
        // 'real' keys.
        const int key = event.KeyInput.Key ? event.KeyInput.Key
                                           : event.KeyInput.Char+1024;

        if (event.KeyInput.PressedDown)
        {
            // escape is a little special
            if (key == KEY_ESCAPE)
            {
                StateManager::get()->escapePressed();
                return EVENT_BLOCK;
            }
            // 'backspace' in a text control must never be mapped, since user
            // can be in a text area trying to erase text (and if it's mapped
            // to rescue that would dismiss the dialog instead of erasing a
            // single letter). Same for spacebar. Same for letters.
            if (GUIEngine::isWithinATextBox())
            {
                if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT)
                {
                    return EVENT_LET;
                }
                if (key >= KEY_KEY_0 && key <= KEY_KEY_Z)
                {
                    return EVENT_LET;
                }
            }

            const bool wasInTextBox = GUIEngine::isWithinATextBox();

            dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key,
                          Input::AD_POSITIVE, Input::MAX_VALUE,
                          event.KeyInput.Shift);

            // if this action took us into a text box, don't let event continue
            // (FIXME not the cleanest solution)
            if (!wasInTextBox && GUIEngine::isWithinATextBox())
            {
                return EVENT_BLOCK;
            }

        }
        else
        {
            // 'backspace' in a text control must never be mapped, since user
            // can be in a text area trying to erase text (and if it's mapped
            // to rescue that would dismiss the dialog instead of erasing a
            // single letter). Same for spacebar. Same for letters.
            if (GUIEngine::isWithinATextBox())
            {
                if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT)
                {
                    return EVENT_LET;
                }
                if (key >= KEY_KEY_0 && key <= KEY_KEY_Z)
                {
                    return EVENT_LET;
                }
            }

            dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key,
                          Input::AD_POSITIVE, 0, event.KeyInput.Shift);
            return EVENT_BLOCK; // Don't propagate key up events
        }
    }
    // Use the mouse to change the looking direction when first person view is activated
    else if (event.EventType == EET_MOUSE_INPUT_EVENT)
    {
        const int type = event.MouseInput.Event;

        if (type == EMIE_MOUSE_MOVED)
        {
            if (Camera::isFPS())
            {
                Camera *cam = Camera::getActiveCamera();
                // Center of the screen
                core::vector2df screen_size = irr_driver->getCurrentScreenSize();
                int mid_x = (int) screen_size.X / 2;
                int mid_y = (int) screen_size.Y / 2;
                // Relative mouse movement
                int diff_x = event.MouseInput.X - m_mouse_val_x;
                int diff_y = event.MouseInput.Y - m_mouse_val_y;
                float mouse_x = ((float) diff_x) *
                    UserConfigParams::m_fpscam_direction_speed;
                float mouse_y = ((float) diff_y) *
                    -UserConfigParams::m_fpscam_direction_speed;

                // No movement the first time it's used
                // At the moment there's also a hard limit because the mouse
                // gets reset to the middle of the screen and sometimes there
                // are more events fired than expected.
                if (m_mouse_val_x != -1 &&
                   (diff_x + diff_y) < 100 && (diff_x + diff_y) > -100)
                {
                    // Rotate camera
                    cam->applyMouseMovement(mouse_x, mouse_y);

                    // Reset mouse position to the middle of the screen when
                    // the mouse is far away
                    if (event.MouseInput.X < mid_x / 2 ||
                        event.MouseInput.X > (mid_x + mid_x / 2) ||
                        event.MouseInput.Y < mid_y / 2 ||
                        event.MouseInput.Y > (mid_y + mid_y / 2))
                    {
                        irr_driver->getDevice()->getCursorControl()->setPosition(mid_x, mid_y);
                        m_mouse_val_x = mid_x;
                        m_mouse_val_y = mid_y;
                    }
                    else
                    {
                        m_mouse_val_x = event.MouseInput.X;
                        m_mouse_val_y = event.MouseInput.Y;
                    }
                }
                else
                {
                    m_mouse_val_x = event.MouseInput.X;
                    m_mouse_val_y = event.MouseInput.Y;
                }
                return EVENT_BLOCK;
            }
            else
                // Reset mouse position
                m_mouse_val_x = m_mouse_val_y = -1;
        }
        else if (type == EMIE_MOUSE_WHEEL)
        {
            if (Camera::isFPS())
            {
                // Use scrolling to change the maximum speed
                // Only test if it's more or less than 0 as it seems to be not
                // reliable accross more platforms.
                Camera *cam = Camera::getActiveCamera();
                if (event.MouseInput.Wheel < 0)
                {
                    float vel = cam->getMaximumVelocity() - 3;
                    if (vel < 0.0f)
                        vel = 0.0f;
                    cam->setMaximumVelocity(vel);
                }
                else if (event.MouseInput.Wheel > 0)
                {
                    cam->setMaximumVelocity(cam->getMaximumVelocity() + 3);
                }
            }
        }

        /*
        EMIE_LMOUSE_PRESSED_DOWN    Left mouse button was pressed down.
        EMIE_RMOUSE_PRESSED_DOWN    Right mouse button was pressed down.
        EMIE_MMOUSE_PRESSED_DOWN    Middle mouse button was pressed down.
        EMIE_LMOUSE_LEFT_UP     Left mouse button was left up.
        EMIE_RMOUSE_LEFT_UP     Right mouse button was left up.
        EMIE_MMOUSE_LEFT_UP     Middle mouse button was left up.
        EMIE_MOUSE_MOVED    The mouse cursor changed its position.
        EMIE_MOUSE_WHEEL    The mouse wheel was moved. Use Wheel value in
                            event data to find out in what direction and
                            how fast.
         */
    }

    // block events in all modes but initial menus (except in text boxes to
    // allow typing, and except in modal dialogs in-game)
    // FIXME: 1) that's awful logic 2) that's not what the code below does,
    // events are never blocked in menus
    if (getDeviceManager()->getAssignMode() != NO_ASSIGN &&
        !GUIEngine::isWithinATextBox() &&
        (!GUIEngine::ModalDialog::isADialogActive() &&
        StateManager::get()->getGameState() == GUIEngine::GAME))
    {
        return EVENT_BLOCK;
    }
    else
    {
        return EVENT_LET;
    }
}