src/Graphics/Input.hh
author Tero Marttila <terom@fixme.fi>
Thu, 22 Jan 2009 00:28:26 +0200
branchnew_graphics
changeset 416 38cba347a3a9
parent 414 cede5463b845
permissions -rw-r--r--
clean up InputHandler/GameView to use signals for input
#ifndef GRAPHICS_INPUT_HH
#define GRAPHICS_INPUT_HH

#include "../Input.hh"
#include "../Timer.hh"
#include "../Types.hh"

#include <ClanLib/signals.h>
#include <ClanLib/Display/input_device.h>
#include <ClanLib/Display/keys.h>

#include <list>

namespace graphics
{

/**
 * Flags to control input behaviour
 */
enum InputFlagBits {
    /** Default */

    /**
     * The bit is not limited, i.e. it is set every time if it's present
     */
    INPUT_FLAG_UNLIMITED    = 0x0000,

    /**
     * The bit is repeat-limited using INPUT_REPEAT_DELAY
     */
    INPUT_FLAG_SLOWREPEAT   = 0x0001,

    /**
     * The bit is repeat-limited using an infinite delay, i.e. you must release the key to trigger it again
     */
    INPUT_FLAG_NOREPEAT     = 0x0002,
};


/**
 * The bits used in the GuiInput bitmask, each represents something handled locally by Graphics.
 */
enum GuiInputBit {
    GUI_INPUT_NONE              = 0x0000,

    GUI_INPUT_QUIT              = 0x0001,
    GUI_INPUT_DISPLAY_WEAPON    = 0x0002,
    GUI_INPUT_DEBUG_PLAYER      = 0x0004,

    GUI_INPUT_TOGGLE_FULLSCREEN = 0x0008,
};

/**
 * Bitmask of InputFlagBits
 */
typedef uint8_t InputFlags;

/**
 * Bitmask for GuiInputBits
 *
 * @see GuiInputBit
 */
typedef uint16_t GuiInput;

/**
 * Keymap definition struct
 */
template <typename BitEnumType> struct InputKeymapEntry {
    /**
     * The input bit to set if present
     */
    BitEnumType input;

    /**
     * Flags to use
     *
     * @see InputFlagBits
     */
    InputFlags flags;

    /**
     * Up to two keycodes to check
     */
    int keycodes[2];
};


/**
 * A InputKeyRepeatQueue entry, this contains the input bit value itself, and then the remaining expire time
 */
template <typename BitEnumType> struct InputKeyRepeatEntry {
        BitEnumType value;

        /**
         * The remaining expire time. If this is zero, it never expires
         */
        TimeMS expire;

    public:
        InputKeyRepeatEntry (BitEnumType value, TimeMS expire);
        
        /**
         * Since priority_queue always gives the greatest item, the one with the longest expire is the least item
         */
        bool operator< (const struct InputKeyRepeatEntry &other);
        
        /**
         * Decrements expire, returning true if it has now expired, false otherwise. Always returns false if expire is
         * zero.
         */
        bool updateExpired (TimeMS dt);
};

/**
 * A InputKeyRepeatQueue maintains a list of InputKeyRepeatEntry's, lets you add new input values, find old ones,
 * and update the list
 */
template <typename BitEnumType> class InputKeyRepeatQueue {
    private:
        TimeMS expire;

        typedef InputKeyRepeatEntry<BitEnumType> entry_type;

        std::list<entry_type> list;

        typedef typename std::list<entry_type>::iterator list_iterator;

    public:
        /**
         * Constructs this queue to contain entries with the given expiry time
         */
        InputKeyRepeatQueue (TimeMS expire);
        
        /**
         * Push a new input bit onto the queue.
         *
         * If expire is true, the bit will automatically expire after our expire time, otherwise, it iwll never expire
         * until forget()'d
         */
        void push (BitEnumType bit, bool expire = true);

        /**
         * Remove any entry for the given bit
         */
        void forget (BitEnumType bit);

        /**
         * Checks if the given input is in the queue
         */
        bool find (BitEnumType bit);
        
        /**
         * Updates the list, removing expired items
         */
        void update (TimeMS dt);

        /**
         * Clear the queue completely
         */
        void clear (void);
};

/**
 * An InputHandler uses an InputKeymapEntry to maintain a BitMaskType of current inputs
 */
template <typename BitEnumType, typename BitMaskType> class InputHandler {
    private:
        /**
         * The keyboard that we read input from
         */
        CL_InputDevice &keyboard;
        
        /**
         * The keymap that we use
         */
        InputKeymapEntry<BitEnumType> *keymap;
        
        /**
         * The current bitmask value
         */
        BitMaskType value;

        /**
         * The previous value, used to detect key-up. This also includes keys that were filtered out
         */
        BitMaskType prev_value;

        /**
         * How long the bitmask was held...
         */
        TimeMS dt;
        
        /**
         * The KeyRepeatQueue
         */
        InputKeyRepeatQueue<BitEnumType> queue;

        /**
         * Are we enabled or not?
         */
        bool _enabled;

        /**
         * The keyevent signal
         */
        CL_Signal_v2<BitMaskType, TimeMS> _sig_input;
    
    public:
        /**
         * Constructs the InputHandler using the given keyboard, keymap and key-repeat expire time.
         *
         * The InputHandler is initially disabled, and must be enabled using enable() for use. 
         */
        InputHandler (CL_InputDevice &keyboard, InputKeymapEntry<BitEnumType> *keymap, TimeMS keyrepeat_expire);
    
    private: 
        /**
         * Returns true if the keycode is valid, false if not.
         *
         * A positive keycode requires that the keycode be active, a negative keycode that the keycode be inactive,
         * and a zero keycode always returns true.
         *
         * @param keycode A positive keycode to check that it's set, negative keycode to check that it's not set, or zero
         * @returns bool true if positive+set/negavtive+notset/zero, false otherwise
         */
        bool checkKeycode (int keycode);
    
    public:
        /**
         * Updates the keyrepeat queue
         */
        void update (TimeMS dt);

        /**
         * Reads the current input mask value and length into mask and dt, and reset ours to zero.
         *
         * It is an error to attempt to read input while disabled.
         *
         * @param mask our BitMaskType value is returned using this
         * @param dt how long the input was held for
         */
        void readValue (BitMaskType &mask, TimeMS &dt);
        
        /**
         * Enables this input handler, collecting bits in value
         */
        void enable (void);

        /**
         * Disables this input handler, zeroing any state, so that enable() works cleanly
         */
        void disable (void);

        /**
         * Current enable/disable state
         */
        bool enabled (void) const { return _enabled; }

        /**
         * This signal is triggered whenever there is nonzero input, or when the input goes to zero
         */
        CL_Signal_v2<BitMaskType, TimeMS>& sig_input (void) { return _sig_input; }
};

/**
 * Handles reading input from a keyboard and mapping it to PlayerInput/GuiInput bitmasks
 */
class Input {
    protected:
        /**
         * The keyboard device that we use
         */
        CL_InputDevice &keyboard;

        /**
         * Our update timer
         */
        Timer update_timer;

        CL_SlotContainer slots;
    
    public:        
        /**
         * Our PlayerInput
         */
        InputHandler<PlayerInputBit, PlayerInput> player;
        
        /**
         * Our GuiInput
         */
        InputHandler<GuiInputBit, GuiInput> gui;

    public:
        /**
         * Build the input handler using the given keyboard and the default keymaps
         */
        Input (CL_InputDevice &keyboard);

    public:
        /**
         * Reads the current PlayerInput value via mask, and the length of the input via dt
         */
        void readPlayerInput (PlayerInput &mask, TimeMS &dt);

        /**
         * Reads the current GuiInput mask and returns it
         */
        GuiInput readGuiInput (void);
};

}



/*
 * Public template class method definitions
 */
#include <cassert>

namespace graphics
{

template <typename BitEnumType, typename BitMaskType> 
void InputHandler<BitEnumType, BitMaskType>::enable (void) {
    // sanity-check
    assert(!_enabled);

    // update state
    _enabled = true;
}

template <typename BitEnumType, typename BitMaskType> 
void InputHandler<BitEnumType, BitMaskType>::disable (void) {
    // sanity-check
    assert(_enabled);

    // update state
    value = prev_value = 0;
    _enabled = false;

    // and clear keyrepeat list
    queue.clear();
}
       

}

#endif