# HG changeset patch # User Tero Marttila # Date 1232575373 -7200 # Node ID cede5463b845ff8e6b71c0bc7e1bd2d119d80f07 # Parent 7dddc163489a3adae5a88f79164ba9cddfc30269 port Input to new Graphics, still a bit hacky, but everything seems to work now diff -r 7dddc163489a -r cede5463b845 src/GameState.hh --- a/src/GameState.hh Wed Jan 21 23:25:29 2009 +0200 +++ b/src/GameState.hh Thu Jan 22 00:02:53 2009 +0200 @@ -4,10 +4,9 @@ class GameState; #include "PhysicsWorld.hh" +#include "Terrain.hh" #include "Player.hh" #include "Projectile.hh" -#include "Rope.hh" -#include "Input.hh" #include "Config.hh" #include "Graphics/Drawable.hh" diff -r 7dddc163489a -r cede5463b845 src/Graphics/Display.cc --- a/src/Graphics/Display.cc Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Graphics/Display.cc Thu Jan 22 00:02:53 2009 +0200 @@ -60,7 +60,7 @@ view->resize(PixelArea(0, 0, get_width(), get_height())); } -void Display::toggle_fullscreen (void) { +void Display::toggleFullscreen (void) { if (is_fullscreen()) { // enter windowed mode set_windowed(); diff -r 7dddc163489a -r cede5463b845 src/Graphics/Display.hh --- a/src/Graphics/Display.hh Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Graphics/Display.hh Thu Jan 22 00:02:53 2009 +0200 @@ -92,7 +92,7 @@ /** * Shift back and forth between fullscreen and windowed mode, retaining resolutions */ - void toggle_fullscreen (void); + void toggleFullscreen (void); private: /** diff -r 7dddc163489a -r cede5463b845 src/Graphics/GameView.cc --- a/src/Graphics/GameView.cc Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Graphics/GameView.cc Thu Jan 22 00:02:53 2009 +0200 @@ -18,6 +18,9 @@ // insert message message_view.add_message(CL_Color::white, "Hello World!"); + + // enable GUI input + graphics->input.gui.enable(); } void GameView::setPlayer (LocalPlayer *player) { @@ -28,8 +31,37 @@ // build the info_view as well info_view = new PlayerInfoView(getInfoViewArea(), player); + + // enable player input + graphics->input.player.enable(); } +void GameView::handleInput (GuiInput flags, TimeMS dt) { + // ignore timing info + (void) dt; + + // update our flags + this->flags = flags; + + // quit? + if (flags & GUI_INPUT_QUIT) { + graphics->engine.stop(); + return; + } + + // dump player debug info on stderr + if ((flags & GUI_INPUT_DEBUG_PLAYER) && player) { + player->printDebugInfo(); + + message_view.add_message(CL_Color::green, "..."); + } + + // toggle fullscreen? + if (flags & GUI_INPUT_TOGGLE_FULLSCREEN) + graphics->display.toggleFullscreen(); +} + + /* * Helper function for Camera */ @@ -49,6 +81,19 @@ void GameView::draw (Display &display) { CL_GraphicContext *gc = display.get_gc(); + + // XXX: these should not be done from here + handleInput(graphics->input.readGuiInput(), 0); + + // XXX: this should /really/ be somewhere else + if (player) { + PlayerInput input; + TimeMS dt; + + graphics->input.readPlayerInput(input, dt); + + player->handleInput(input, dt); + } // calculate camera PixelCoordinate camera(0, 0); diff -r 7dddc163489a -r cede5463b845 src/Graphics/GameView.hh --- a/src/Graphics/GameView.hh Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Graphics/GameView.hh Thu Jan 22 00:02:53 2009 +0200 @@ -4,15 +4,17 @@ #include "Drawable.hh" #include "PlayerInfoView.hh" #include "MessageView.hh" +#include "Input.hh" #include "../GameState.hh" -#include "../Input.hh" namespace graphics { /** - * This is the main in-game view, which is what the player sees when they are playing + * This is the main in-game view, which is what the player sees when they are playing. + * + * This enables graphics->input.gui/player */ class GameView : public View { protected: @@ -63,6 +65,11 @@ return PixelArea(400, area.bottom - 100, area.right, area.bottom); } + /** + * Handle GUI input + */ + void handleInput (GuiInput flags, TimeMS dt); + public: /** diff -r 7dddc163489a -r cede5463b845 src/Graphics/Graphics.cc --- a/src/Graphics/Graphics.cc Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Graphics/Graphics.cc Thu Jan 22 00:02:53 2009 +0200 @@ -10,8 +10,11 @@ Graphics *graphics = NULL; Graphics::Graphics (Engine &engine, CL_ResourceManager &resources, const DisplayConfig &display_config) : + engine(engine), display(display_config), // display must be set up before fonts, due to implicit CL_DisplayWindow - engine(engine), display(display_config), fonts(resources) + fonts(resources), + // get the InputContext from display + input(display.get_ic()->get_keyboard()) { assert(!graphics); diff -r 7dddc163489a -r cede5463b845 src/Graphics/Graphics.hh --- a/src/Graphics/Graphics.hh Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Graphics/Graphics.hh Thu Jan 22 00:02:53 2009 +0200 @@ -11,6 +11,7 @@ #include "../Engine.hh" #include "Display.hh" #include "FontManager.hh" +#include "Input.hh" #include "GameView.hh" namespace graphics @@ -35,6 +36,13 @@ * For loading fonts */ FontManager fonts; + + /** + * Input handling + * + * XXX: move Input class into this? + */ + Input input; /** * Initialize the graphics subsystem diff -r 7dddc163489a -r cede5463b845 src/Graphics/Input.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Graphics/Input.cc Thu Jan 22 00:02:53 2009 +0200 @@ -0,0 +1,246 @@ + +#include "Input.hh" +#include "../Error.hh" +#include "../Config.hh" + +#include + +namespace graphics +{ + +InputKeymapEntry INPUT_PLAYER_KEYMAP[] = { + { INPUT_AIM_UP, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_UP } }, + { INPUT_AIM_DOWN, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_DOWN } }, + { INPUT_MOVE_LEFT, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_LEFT } }, + { INPUT_MOVE_RIGHT, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_RIGHT } }, + { INPUT_JUMP, INPUT_FLAG_SLOWREPEAT, { -CL_KEY_ENTER, CL_KEY_RSHIFT } }, + { INPUT_DIG, INPUT_FLAG_NOREPEAT, { CL_KEY_LEFT, CL_KEY_RIGHT } }, + { INPUT_SHOOT, INPUT_FLAG_UNLIMITED, { CL_KEY_RCONTROL, 0 } }, + { INPUT_CHANGE_PREV, INPUT_FLAG_SLOWREPEAT, { CL_KEY_ENTER, CL_KEY_LEFT } }, + { INPUT_CHANGE_NEXT, INPUT_FLAG_SLOWREPEAT, { CL_KEY_ENTER, CL_KEY_RIGHT } }, + { INPUT_ROPE, INPUT_FLAG_NOREPEAT, { CL_KEY_ENTER, CL_KEY_RSHIFT } }, + { INPUT_UNROPE, INPUT_FLAG_SLOWREPEAT, { -CL_KEY_ENTER, CL_KEY_RSHIFT } }, + { INPUT_ROPE_UP, INPUT_FLAG_UNLIMITED, { CL_KEY_ENTER, CL_KEY_UP } }, + { INPUT_ROPE_DOWN, INPUT_FLAG_UNLIMITED, { CL_KEY_ENTER, CL_KEY_DOWN } }, + { INPUT_SUICIDE, INPUT_FLAG_NOREPEAT, { CL_KEY_LCONTROL, CL_KEY_K } }, + { INPUT_NONE, 0, { 0, 0 } } +}; + +InputKeymapEntry INPUT_GUI_KEYMAP[] = { + { GUI_INPUT_QUIT, 0, { CL_KEY_ESCAPE, 0 } }, + { GUI_INPUT_DISPLAY_WEAPON, 0, { CL_KEY_ENTER, 0 } }, + { GUI_INPUT_DEBUG_PLAYER, 0, { CL_KEY_I, 0 } }, + { GUI_INPUT_TOGGLE_FULLSCREEN, INPUT_FLAG_NOREPEAT,{ CL_KEY_LCONTROL, CL_KEY_F } }, + { GUI_INPUT_NONE, 0, { 0, 0, } } +}; + +/* + * InputKeyRepeatEntry + */ +template +InputKeyRepeatEntry::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : + value(value), expire(expire) +{ + +} + +template +bool InputKeyRepeatEntry::operator< (const struct InputKeyRepeatEntry &other) { + return other.expire > expire; +} + +template +bool InputKeyRepeatEntry::updateExpired (TimeMS dt) { + if (expire == 0) + return false; + + expire -= dt; + + return (expire <= 0); +} + +/* + * InputKeyRepeatQueue + */ +template +InputKeyRepeatQueue::InputKeyRepeatQueue (TimeMS expire) : + expire(expire) +{ + +} + +template +void InputKeyRepeatQueue::push (BitEnumType bit, bool expire) { + list.push_back(InputKeyRepeatEntry(bit, expire ? this->expire : 0)); +} + +template +void InputKeyRepeatQueue::forget (BitEnumType bit) { + // go through the list, looking for it + for (list_iterator it = list.begin(); it != list.end(); it++) { + if (it->value == bit) { + // found, erase it and return + list.erase(it); + + return; + } + } +} + +template +bool InputKeyRepeatQueue::find (BitEnumType bit) { + for (list_iterator it = list.begin(); it != list.end(); it++) { + if (it->value == bit) + return true; + } + + return false; +} + +template +void InputKeyRepeatQueue::update (TimeMS dt) { + list_iterator it = list.begin(); + + // go through each entry, updateExpired and remove if expired + while (it != list.end()) { + if (it->updateExpired(dt)) + it = list.erase(it); + else + it++; + } +} + +template +void InputKeyRepeatQueue::clear (void) { + // just clear our list of events + list.clear(); +} + +/* + * InputHandler + */ +template +InputHandler::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *keymap, TimeMS keyrepeat_expire) : + keyboard(keyboard), + keymap(keymap), + value(0), + prev_value(0), + dt(0), + queue(keyrepeat_expire), + _enabled(false) +{ + +} + +template +bool InputHandler::checkKeycode (int keycode) { + if (keycode > 0) + return keyboard.get_keycode(keycode); + + else if (keycode < 0) + return !keyboard.get_keycode(-keycode); + + else // == 0 + return true; +} + +template +void InputHandler::update (TimeMS dt) { + // ignore if not enabled + if (!_enabled) + return; + + // all bits that are held down, even those ignored + BitMaskType raw_value = 0; + + // update the key-repeat queue + queue.update(dt); + + // then go through the keymap + for (InputKeymapEntry *e = keymap; e->input != 0; e++) { + // check if we've got the correct keycodes + if (checkKeycode(e->keycodes[0]) && checkKeycode(e->keycodes[1])) { + // set raw_value + raw_value |= e->input; + + if (e->flags & INPUT_FLAG_SLOWREPEAT) { + // repeat, but slowly + if (!(prev_value & e->input)) { + // we've released the key earlier, move it to the back of the queue + queue.forget(e->input); + queue.push(e->input); + + } else if (queue.find(e->input)) { + // it's still in the queue, so ignore, but set it in ignore_value + continue; + + } else { + // ok, but add it to the queue + queue.push(e->input); + } + + } else if (e->flags & INPUT_FLAG_NOREPEAT) { + // do not repeat at all + if (prev_value & e->input) { + // ignore repeats + continue; + } + } + + // set bit in masks + this->value |= e->input; + } + } + + // update prev_value, also adding ignored values + prev_value = raw_value; + + // then increment our dt + this->dt += dt; +} + +template +void InputHandler::readValue (BitMaskType &mask, TimeMS &dt) { + // throw exception if disabled + if (!_enabled) + throw Error("InputHandler is disabled"); + + // copy to args + mask = this->value; + dt = this->dt; + + this->value = 0; + this->dt = 0; +} + +/** + * Input + */ +Input::Input (CL_InputDevice &keyboard) : + keyboard(keyboard), + update_timer(INPUT_POLL_INTERVAL), + player(keyboard, INPUT_PLAYER_KEYMAP, INPUT_REPEAT_DELAY), + gui(keyboard, INPUT_GUI_KEYMAP, INPUT_REPEAT_DELAY) +{ + // connect timer + slots.connect(update_timer.sig_tick(), &player, &InputHandler::update); + slots.connect(update_timer.sig_tick(), &gui, &InputHandler::update); + + // enable timer + update_timer.start(); +} + +void Input::readPlayerInput (PlayerInput &mask, TimeMS &dt) { + player.readValue(mask, dt); +} + +GuiInput Input::readGuiInput (void) { + GuiInput mask; + TimeMS dt; + + gui.readValue(mask, dt); + + return mask; +} + +} diff -r 7dddc163489a -r cede5463b845 src/Graphics/Input.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Graphics/Input.hh Thu Jan 22 00:02:53 2009 +0200 @@ -0,0 +1,338 @@ +#ifndef GRAPHICS_INPUT_HH +#define GRAPHICS_INPUT_HH + +#include "../Input.hh" +#include "../Timer.hh" +#include "../Types.hh" + +#include +#include + +#include + +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 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 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 class InputKeyRepeatQueue { + private: + TimeMS expire; + + typedef InputKeyRepeatEntry entry_type; + + std::list list; + + typedef typename std::list::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 class InputHandler { + private: + /** + * The keyboard that we read input from + */ + CL_InputDevice &keyboard; + + /** + * The keymap that we use + */ + InputKeymapEntry *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 queue; + + /** + * Are we enabled or not? + */ + bool _enabled; + + 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 *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; } +}; + +/** + * 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 player; + + /** + * Our GuiInput + */ + InputHandler 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 + +namespace graphics +{ + +template +void InputHandler::enable (void) { + // sanity-check + assert(!_enabled); + + // update state + _enabled = true; +} + +template +void InputHandler::disable (void) { + // sanity-check + assert(_enabled); + + // update state + value = prev_value = 0; + _enabled = false; + + // and clear keyrepeat list + queue.clear(); +} + + +} + +#endif diff -r 7dddc163489a -r cede5463b845 src/Input.cc --- a/src/Input.cc Wed Jan 21 23:25:29 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ - -#include "Input.hh" -#include "Engine.hh" -#include "Config.hh" - -InputKeymapEntry INPUT_PLAYER_KEYMAP[] = { - { INPUT_AIM_UP, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_UP } }, - { INPUT_AIM_DOWN, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_DOWN } }, - { INPUT_MOVE_LEFT, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_LEFT } }, - { INPUT_MOVE_RIGHT, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_RIGHT } }, - { INPUT_JUMP, INPUT_FLAG_SLOWREPEAT, { -CL_KEY_ENTER, CL_KEY_RSHIFT } }, - { INPUT_DIG, INPUT_FLAG_NOREPEAT, { CL_KEY_LEFT, CL_KEY_RIGHT } }, - { INPUT_SHOOT, INPUT_FLAG_UNLIMITED, { CL_KEY_RCONTROL, 0 } }, - { INPUT_CHANGE_PREV, INPUT_FLAG_SLOWREPEAT, { CL_KEY_ENTER, CL_KEY_LEFT } }, - { INPUT_CHANGE_NEXT, INPUT_FLAG_SLOWREPEAT, { CL_KEY_ENTER, CL_KEY_RIGHT } }, - { INPUT_ROPE, INPUT_FLAG_NOREPEAT, { CL_KEY_ENTER, CL_KEY_RSHIFT } }, - { INPUT_UNROPE, INPUT_FLAG_SLOWREPEAT, { -CL_KEY_ENTER, CL_KEY_RSHIFT } }, - { INPUT_ROPE_UP, INPUT_FLAG_UNLIMITED, { CL_KEY_ENTER, CL_KEY_UP } }, - { INPUT_ROPE_DOWN, INPUT_FLAG_UNLIMITED, { CL_KEY_ENTER, CL_KEY_DOWN } }, - { INPUT_SUICIDE, INPUT_FLAG_NOREPEAT, { CL_KEY_LCONTROL, CL_KEY_K } }, - { INPUT_NONE, 0, { 0, 0 } } -}; - -InputKeymapEntry INPUT_GUI_KEYMAP[] = { - { GUI_INPUT_QUIT, 0, { CL_KEY_ESCAPE, 0 } }, - { GUI_INPUT_DISPLAY_WEAPON, 0, { CL_KEY_ENTER, 0 } }, - { GUI_INPUT_DEBUG_PLAYER, 0, { CL_KEY_I, 0 } }, - { GUI_INPUT_TOGGLE_FULLSCREEN, INPUT_FLAG_NOREPEAT,{ CL_KEY_LCONTROL, CL_KEY_F } }, - { GUI_INPUT_NONE, 0, { 0, 0, } } -}; - -/* - * InputKeyRepeatEntry - */ -template -InputKeyRepeatEntry::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : - value(value), expire(expire) -{ - -} - -template -bool InputKeyRepeatEntry::operator< (const struct InputKeyRepeatEntry &other) { - return other.expire > expire; -} - -template -bool InputKeyRepeatEntry::updateExpired (TimeMS dt) { - if (expire == 0) - return false; - - expire -= dt; - - return (expire <= 0); -} - -/* - * InputKeyRepeatQueue - */ -template -InputKeyRepeatQueue::InputKeyRepeatQueue (TimeMS expire) : - expire(expire) -{ - -} - -template -void InputKeyRepeatQueue::push (BitEnumType bit, bool expire) { - list.push_back(InputKeyRepeatEntry(bit, expire ? this->expire : 0)); -} - -template -void InputKeyRepeatQueue::forget (BitEnumType bit) { - // go through the list, looking for it - for (list_iterator it = list.begin(); it != list.end(); it++) { - if (it->value == bit) { - // found, erase it and return - list.erase(it); - - return; - } - } -} - -template -bool InputKeyRepeatQueue::find (BitEnumType bit) { - for (list_iterator it = list.begin(); it != list.end(); it++) { - if (it->value == bit) - return true; - } - - return false; -} - -template -void InputKeyRepeatQueue::update (TimeMS dt) { - list_iterator it = list.begin(); - - // go through each entry, updateExpired and remove if expired - while (it != list.end()) { - if (it->updateExpired(dt)) - it = list.erase(it); - else - it++; - } -} - -/* - * InputHandler - */ -template -InputHandler::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *keymap, TimeMS keyrepeat_expire) : - keyboard(keyboard), - keymap(keymap), - value(0), - prev_value(0), - dt(0), - queue(keyrepeat_expire) -{ - -} - -template -bool InputHandler::checkKeycode (int keycode) { - if (keycode > 0) - return keyboard.get_keycode(keycode); - - else if (keycode < 0) - return !keyboard.get_keycode(-keycode); - - else // == 0 - return true; -} - -template -void InputHandler::readValue (BitMaskType &mask, TimeMS &dt) { - // copy to args - mask = this->value; - dt = this->dt; - - this->value = 0; - this->dt = 0; -} - -template -void InputHandler::update (TimeMS dt) { - // all bits that are held down, even those ignored - BitMaskType raw_value = 0; - - // update the key-repeat queue - queue.update(dt); - - // then go through the keymap - for (InputKeymapEntry *e = keymap; e->input != 0; e++) { - // check if we've got the correct keycodes - if (checkKeycode(e->keycodes[0]) && checkKeycode(e->keycodes[1])) { - // set raw_value - raw_value |= e->input; - - if (e->flags & INPUT_FLAG_SLOWREPEAT) { - // repeat, but slowly - if (!(prev_value & e->input)) { - // we've released the key earlier, move it to the back of the queue - queue.forget(e->input); - queue.push(e->input); - - } else if (queue.find(e->input)) { - // it's still in the queue, so ignore, but set it in ignore_value - continue; - - } else { - // ok, but add it to the queue - queue.push(e->input); - } - - } else if (e->flags & INPUT_FLAG_NOREPEAT) { - // do not repeat at all - if (prev_value & e->input) { - // ignore repeats - continue; - } - } - - // set bit in masks - this->value |= e->input; - } - } - - // update prev_value, also adding ignored values - prev_value = raw_value; - - // then increment our dt - this->dt += dt; -} - -/** - * Input - */ -Input::Input (CL_InputDevice &keyboard) : - keyboard(keyboard), - update_timer(INPUT_POLL_INTERVAL), - player(keyboard, INPUT_PLAYER_KEYMAP, INPUT_REPEAT_DELAY), - gui(keyboard, INPUT_GUI_KEYMAP, INPUT_REPEAT_DELAY) -{ - // connect timer - slots.connect(update_timer.sig_tick(), &player, &InputHandler::update); - slots.connect(update_timer.sig_tick(), &gui, &InputHandler::update); - - // enable timer - update_timer.start(); -} - -void Input::readPlayerInput (PlayerInput &mask, TimeMS &dt) { - player.readValue(mask, dt); -} - -GuiInput Input::readGuiInput (void) { - GuiInput mask; - TimeMS dt; - - gui.readValue(mask, dt); - - return mask; -} - diff -r 7dddc163489a -r cede5463b845 src/Input.hh --- a/src/Input.hh Wed Jan 21 23:25:29 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,301 +0,0 @@ -#ifndef INPUT_HH -#define INPUT_HH - -#include "Timer.hh" -#include "Types.hh" - -#include -#include -#include - -// const TimeMS INPUT_INTERVAL_MS = 20; - -/** - * 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 PlayerInput bitmask, each represents a separate action handled by LocalPlayer::handleInput. - * - * @see LocalPlayer::handleInput - */ -enum PlayerInputBit { - INPUT_NONE = 0x0000, - - INPUT_AIM_UP = 0x0001, - INPUT_AIM_DOWN = 0x0002, - - INPUT_MOVE_LEFT = 0x0004, - INPUT_MOVE_RIGHT = 0x0008, - - INPUT_JUMP = 0x0010, - INPUT_DIG = 0x0020, - INPUT_SHOOT = 0x0040, - INPUT_CHANGE_NEXT = 0x0080, - INPUT_CHANGE_PREV = 0x0100, - INPUT_ROPE = 0x0200, - INPUT_UNROPE = 0x0400, - INPUT_ROPE_UP = 0x0800, - INPUT_ROPE_DOWN = 0x1000, - - INPUT_SUICIDE = 0x2000, -}; - -/** - * 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 of PlayerInputBits - * - * @see PlayerInputBit - */ -typedef uint16_t PlayerInput; - -/** - * Bitmask for GuiInputBits - * - * @see GuiInputBit - */ -typedef uint16_t GuiInput; - -/** - * Keymap definition struct - */ -template 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 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 class InputKeyRepeatQueue { - private: - TimeMS expire; - - typedef InputKeyRepeatEntry entry_type; - - std::list list; - - typedef typename std::list::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); -}; - -/** - * An InputHandler uses an InputKeymapEntry to maintain a BitMaskType of current inputs - */ -template class InputHandler { - private: - /** - * The keyboard that we read input from - */ - CL_InputDevice &keyboard; - - /** - * The keymap that we use - */ - InputKeymapEntry *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 queue; - - public: - /** - * Constructs the InputHandler using the given keyboard, keymap and key-repeat expire time - */ - InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *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 - * - * @param mask our BitMaskType value is returned using this - * @param dt how long the input was held for - */ - void readValue (BitMaskType &mask, TimeMS &dt); - -}; - -/** - * 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; - - /** - * Our PlayerInput - */ - InputHandler player; - - /** - * Our GuiInput - */ - InputHandler 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); -}; - -#endif diff -r 7dddc163489a -r cede5463b845 src/Player.hh --- a/src/Player.hh Wed Jan 21 23:25:29 2009 +0200 +++ b/src/Player.hh Thu Jan 22 00:02:53 2009 +0200 @@ -9,9 +9,9 @@ #include "Projectile.hh" #include "GameState.hh" #include "PhysicsObject.hh" -#include "Input.hh" #include "Rope.hh" #include "Types.hh" +#include "Input.hh" #include "Graphics/Drawable.hh"