#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