terom@221: terom@221: #include "Input.hh" terom@414: #include "../Error.hh" terom@414: #include "../Config.hh" terom@414: terom@414: #include terom@414: terom@414: namespace graphics terom@414: { terom@221: terom@233: InputKeymapEntry INPUT_PLAYER_KEYMAP[] = { terom@319: { INPUT_AIM_UP, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_UP } }, terom@319: { INPUT_AIM_DOWN, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_DOWN } }, terom@319: { INPUT_MOVE_LEFT, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_LEFT } }, terom@319: { INPUT_MOVE_RIGHT, INPUT_FLAG_UNLIMITED, { -CL_KEY_ENTER, CL_KEY_RIGHT } }, terom@319: { INPUT_JUMP, INPUT_FLAG_SLOWREPEAT, { -CL_KEY_ENTER, CL_KEY_RSHIFT } }, terom@311: { INPUT_DIG, INPUT_FLAG_NOREPEAT, { CL_KEY_LEFT, CL_KEY_RIGHT } }, terom@319: { INPUT_SHOOT, INPUT_FLAG_UNLIMITED, { CL_KEY_RCONTROL, 0 } }, terom@319: { INPUT_CHANGE_PREV, INPUT_FLAG_SLOWREPEAT, { CL_KEY_ENTER, CL_KEY_LEFT } }, terom@319: { INPUT_CHANGE_NEXT, INPUT_FLAG_SLOWREPEAT, { CL_KEY_ENTER, CL_KEY_RIGHT } }, terom@311: { INPUT_ROPE, INPUT_FLAG_NOREPEAT, { CL_KEY_ENTER, CL_KEY_RSHIFT } }, terom@319: { INPUT_UNROPE, INPUT_FLAG_SLOWREPEAT, { -CL_KEY_ENTER, CL_KEY_RSHIFT } }, terom@319: { INPUT_ROPE_UP, INPUT_FLAG_UNLIMITED, { CL_KEY_ENTER, CL_KEY_UP } }, terom@319: { INPUT_ROPE_DOWN, INPUT_FLAG_UNLIMITED, { CL_KEY_ENTER, CL_KEY_DOWN } }, terom@311: { INPUT_SUICIDE, INPUT_FLAG_NOREPEAT, { CL_KEY_LCONTROL, CL_KEY_K } }, terom@311: { INPUT_NONE, 0, { 0, 0 } } terom@233: }; terom@233: terom@233: InputKeymapEntry INPUT_GUI_KEYMAP[] = { terom@389: { GUI_INPUT_QUIT, 0, { CL_KEY_ESCAPE, 0 } }, terom@389: { GUI_INPUT_DISPLAY_WEAPON, 0, { CL_KEY_ENTER, 0 } }, terom@389: { GUI_INPUT_DEBUG_PLAYER, 0, { CL_KEY_I, 0 } }, terom@389: { GUI_INPUT_TOGGLE_FULLSCREEN, INPUT_FLAG_NOREPEAT,{ CL_KEY_LCONTROL, CL_KEY_F } }, terom@389: { GUI_INPUT_NONE, 0, { 0, 0, } } terom@233: }; terom@230: terom@311: /* terom@311: * InputKeyRepeatEntry terom@311: */ terom@311: template terom@311: InputKeyRepeatEntry::InputKeyRepeatEntry (BitEnumType value, TimeMS expire) : terom@311: value(value), expire(expire) terom@235: { terom@235: terom@235: } terom@311: terom@311: template terom@311: bool InputKeyRepeatEntry::operator< (const struct InputKeyRepeatEntry &other) { terom@311: return other.expire > expire; terom@311: } terom@311: terom@311: template terom@311: bool InputKeyRepeatEntry::updateExpired (TimeMS dt) { terom@319: if (expire == 0) terom@319: return false; terom@319: terom@311: expire -= dt; terom@311: terom@311: return (expire <= 0); terom@311: } terom@311: terom@311: /* terom@311: * InputKeyRepeatQueue terom@311: */ terom@311: template terom@311: InputKeyRepeatQueue::InputKeyRepeatQueue (TimeMS expire) : terom@311: expire(expire) terom@311: { terom@311: terom@311: } terom@311: terom@311: template terom@319: void InputKeyRepeatQueue::push (BitEnumType bit, bool expire) { terom@319: list.push_back(InputKeyRepeatEntry(bit, expire ? this->expire : 0)); terom@319: } terom@319: terom@319: template terom@319: void InputKeyRepeatQueue::forget (BitEnumType bit) { terom@319: // go through the list, looking for it terom@319: for (list_iterator it = list.begin(); it != list.end(); it++) { terom@319: if (it->value == bit) { terom@319: // found, erase it and return terom@319: list.erase(it); terom@319: terom@319: return; terom@319: } terom@319: } terom@311: } terom@311: terom@311: template terom@311: bool InputKeyRepeatQueue::find (BitEnumType bit) { terom@311: for (list_iterator it = list.begin(); it != list.end(); it++) { terom@311: if (it->value == bit) terom@311: return true; terom@311: } terom@311: terom@311: return false; terom@311: } terom@311: terom@311: template terom@311: void InputKeyRepeatQueue::update (TimeMS dt) { terom@311: list_iterator it = list.begin(); terom@235: terom@311: // go through each entry, updateExpired and remove if expired terom@311: while (it != list.end()) { terom@311: if (it->updateExpired(dt)) terom@311: it = list.erase(it); terom@311: else terom@311: it++; terom@311: } terom@311: } terom@311: terom@414: template terom@414: void InputKeyRepeatQueue::clear (void) { terom@414: // just clear our list of events terom@414: list.clear(); terom@414: } terom@414: terom@311: /* terom@311: * InputHandler terom@311: */ terom@311: template terom@311: InputHandler::InputHandler (CL_InputDevice &keyboard, InputKeymapEntry *keymap, TimeMS keyrepeat_expire) : terom@311: keyboard(keyboard), terom@311: keymap(keymap), terom@311: value(0), terom@319: prev_value(0), terom@311: dt(0), terom@414: queue(keyrepeat_expire), terom@414: _enabled(false) terom@311: { terom@311: terom@311: } terom@311: terom@311: template terom@311: bool InputHandler::checkKeycode (int keycode) { terom@230: if (keycode > 0) terom@230: return keyboard.get_keycode(keycode); terom@230: terom@230: else if (keycode < 0) terom@230: return !keyboard.get_keycode(-keycode); terom@230: terom@230: else // == 0 terom@230: return true; terom@230: } terom@414: terom@311: template terom@311: void InputHandler::update (TimeMS dt) { terom@414: // ignore if not enabled terom@414: if (!_enabled) terom@414: return; terom@414: terom@319: // all bits that are held down, even those ignored terom@416: BitMaskType raw_value = 0, this_value = 0; terom@319: terom@311: // update the key-repeat queue terom@311: queue.update(dt); terom@311: terom@311: // then go through the keymap terom@311: for (InputKeymapEntry *e = keymap; e->input != 0; e++) { terom@311: // check if we've got the correct keycodes terom@311: if (checkKeycode(e->keycodes[0]) && checkKeycode(e->keycodes[1])) { terom@319: // set raw_value terom@319: raw_value |= e->input; terom@319: terom@319: if (e->flags & INPUT_FLAG_SLOWREPEAT) { terom@319: // repeat, but slowly terom@319: if (!(prev_value & e->input)) { terom@319: // we've released the key earlier, move it to the back of the queue terom@319: queue.forget(e->input); terom@319: queue.push(e->input); terom@319: terom@319: } else if (queue.find(e->input)) { terom@319: // it's still in the queue, so ignore, but set it in ignore_value terom@311: continue; terom@319: terom@319: } else { terom@319: // ok, but add it to the queue terom@311: queue.push(e->input); terom@319: } terom@319: terom@319: } else if (e->flags & INPUT_FLAG_NOREPEAT) { terom@319: // do not repeat at all terom@319: if (prev_value & e->input) { terom@319: // ignore repeats terom@319: continue; terom@319: } terom@311: } terom@311: terom@416: // set bit in value mask terom@416: this_value |= e->input; terom@311: } terom@311: } terom@311: terom@416: // signal unless value was and remains zero terom@416: if (this_value || prev_value) { terom@416: // trigger signal terom@416: _sig_input(this_value, dt); terom@416: } terom@416: terom@319: // update prev_value, also adding ignored values terom@319: prev_value = raw_value; terom@319: terom@416: // update our collective value + dt for use with readValue terom@416: // XXX: remove this functionality? terom@416: value |= this_value; terom@311: this->dt += dt; terom@311: } terom@414: terom@414: template terom@414: void InputHandler::readValue (BitMaskType &mask, TimeMS &dt) { terom@414: // throw exception if disabled terom@414: if (!_enabled) terom@414: throw Error("InputHandler is disabled"); terom@311: terom@414: // copy to args terom@414: mask = this->value; terom@414: dt = this->dt; terom@414: terom@414: this->value = 0; terom@414: this->dt = 0; terom@414: } terom@414: terom@311: /** terom@311: * Input terom@311: */ terom@311: Input::Input (CL_InputDevice &keyboard) : terom@311: keyboard(keyboard), terom@311: update_timer(INPUT_POLL_INTERVAL), terom@311: player(keyboard, INPUT_PLAYER_KEYMAP, INPUT_REPEAT_DELAY), terom@311: gui(keyboard, INPUT_GUI_KEYMAP, INPUT_REPEAT_DELAY) terom@311: { terom@311: // connect timer terom@311: slots.connect(update_timer.sig_tick(), &player, &InputHandler::update); terom@311: slots.connect(update_timer.sig_tick(), &gui, &InputHandler::update); terom@311: terom@311: // enable timer terom@311: update_timer.start(); terom@311: } terom@311: terom@311: void Input::readPlayerInput (PlayerInput &mask, TimeMS &dt) { terom@311: player.readValue(mask, dt); terom@233: } terom@233: terom@235: GuiInput Input::readGuiInput (void) { terom@311: GuiInput mask; terom@311: TimeMS dt; terom@311: terom@311: gui.readValue(mask, dt); terom@311: terom@311: return mask; terom@233: } terom@233: terom@414: }