/* client/input.h This file is part of the Osirion project and is distributed under the terms and conditions of the GNU General Public License version 2 */ #include "SDL/SDL.h" #include "audio/audio.h" #include "auxiliary/functions.h" #include "client/action.h" #include "client/client.h" #include "client/input.h" #include "client/joystick.h" #include "client/keyboard.h" #include "client/targets.h" #include "client/video.h" #include "core/core.h" #include "math/functions.h" #include "render/camera.h" #include "render/state.h" #include "render/draw.h" #include "render/dust.h" #include "render/text.h" #include "render/screenshot.h" #include "ui/ui.h" namespace client { core::Cvar *input_mousecontrol = 0; core::Cvar *input_keydelay = 0; core::Cvar *input_keyrepeat = 0; core::Cvar *input_mousedelay = 0; core::Cvar *input_grab = 0; namespace input { //--- local variables --------------------------------------------- // keyboard instance Keyboard *keyboard = 0; // keyboard modifiers shift, ctrl, alt etc int keyboard_modifiers = 0; // last key pressed Key *last_key = 0; float last_key_time = 0; // local controls float local_direction = 0.0f; float local_pitch = 0.0f; float local_thrust = 0.0f; float local_roll = 0.0f; float local_strafe = 0.0f; float local_vstrafe = 0.0f; float local_afterburner = 0.0f; int local_controlflags = core::EntityControlable::ControlFlagNone; // last controlled entity unsigned int last_control = 0; // mouse cursor position int mouse_x = 0; int mouse_y = 0; float mouse_pitch = 0; float mouse_direction = 0; // true if the mouse has control float mouse_control_override_time = 0; bool mouse_control_override = false; bool mouse_control = false; // time the mouse was last moved float mouse_lastmoved = 0; // true if we're using a joystick bool joystick_control = false; // time the joystick last moved float joystick_lastmoved = 0; const float thruster_offset = 0.05f; int mouse_position_x() { return mouse_x; } int mouse_position_y() { return mouse_y; } Key *last_key_pressed() { return last_key; } float mouse_lastmoved_time() { return mouse_lastmoved; } float joystick_lastmoved_time() { return joystick_lastmoved; } //--- engine functions -------------------------------------------- void func_screenshot(std::string const & args) { render::Screenshot::save(); } void func_ui_control(std::string const &args) { if (!core::localcontrol() || core::localplayer()->view()) return; if (input_mousecontrol->value() > 0.0f) { (*input_mousecontrol) = 0.0f; } else { (*input_mousecontrol) = 1.0f; } if (!input_mousecontrol->value()) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); } con_print << "mouse control is " << ((input_mousecontrol->value()) ? "on" : "off") << std::endl; audio::play("ui/control"); } void func_view_next(std::string const &args) { if (!core::localplayer()->view() && core::application()->connected() && core::localcontrol()) { render::Camera::view_next(); local_roll = 0; local_pitch = 0; local_direction = 0; audio::play("ui/view"); } } void func_view_prev(std::string const &args) { if (!core::localplayer()->view() && core::application()->connected() && core::localcontrol()) { render::Camera::view_previous(); local_roll = 0; local_pitch = 0; local_direction = 0; audio::play("ui/view"); } } void func_list_joystick(std::string const &args) { Joystick::list(); } void func_list_actions(std::string const &args) { if (keyboard) keyboard->list_actions(); else con_warn << "Keyboard handler not installed!" << std::endl; } void func_list_keys(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { aux::to_lowercase(keyname); keyboard->bind(keyname, ""); } else { keyboard->list_keys(); } } else { con_warn << "Keyboard handler not installed!" << std::endl; } } void func_list_binds(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { aux::to_lowercase(keyname); keyboard->bind(keyname, ""); } else { keyboard->list_binds(); } } else { con_warn << "Keyboard handler not installed!" << std::endl; } } void func_bind(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { aux::to_lowercase(keyname); if (args.size() > keyname.size() + 1) { keyboard->bind(keyname, args.substr(keyname.size() + 1)); } else { keyboard->bind(keyname, ""); } return; } else { core::Func *func = core::Func::find("bind"); con_print << "Usage: bind " << func->info() << std::endl; } } else { con_warn << "Keyboard handler not installed!" << std::endl; } } void func_unbind(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { keyboard->unbind(keyname); return; } core::Func *func = core::Func::find("unbind"); con_print << "unbind " << func->info() << std::endl; } else { con_warn << "Keyboard handler not installed!" << std::endl; } } //--- input functions --------------------------------------------- void init() { con_print << "^BInitializing input..." << std::endl; keyboard = new Keyboard(); SDL_ShowCursor(SDL_DISABLE); SDL_WM_GrabInput(SDL_GRAB_ON); // SDL_EnableUNICODE(1); Joystick::init(); input_mousecontrol = core::Cvar::get("input_mousecontrol", 1.0f, core::Cvar::Archive); input_mousecontrol->set_info("[bool] enable or disable mouse control"); input_keydelay = core::Cvar::get("input_keydelay", 250.0f, core::Cvar::Archive); input_keydelay->set_info("[int] keyboard delay time-out in milliseconds"); input_keyrepeat = core::Cvar::get("input_keyrepeat", 30.0f, core::Cvar::Archive); input_keyrepeat->set_info("[int] keyboard repeat time-out in milliseconds"); input_mousedelay = core::Cvar::get("input_mousedelay", 150.0f, core::Cvar::Archive); input_mousedelay->set_info("[int] mouse click time-out in milliseconds"); input_grab = core::Cvar::get("input_grab", 1.0f, core::Cvar::Archive); input_grab->set_info("[bool] grab input"); if (!input_grab->value()) { SDL_WM_GrabInput(SDL_GRAB_OFF); } core::Func *func = 0; func = core::Func::add("ui_control", func_ui_control); func->set_info("toggle mouse control"); func = core::Func::add("list_joystick", func_list_joystick); func->set_info("list joysticks"); func = core::Func::add("list_actions", func_list_actions); func->set_info("list key action names"); func = core::Func::add("list_keys", func_list_keys); func->set_info("list key names"); func = core::Func::add("list_binds", func_list_binds); func->set_info("list keyb binds"); func = core::Func::add("bind", (core::FuncPtr) func_bind); func->set_info("[key] [str] bind a command to a key"); func = core::Func::add("unbind", func_unbind); func->set_info("[key] unbind a key"); func = core::Func::add("unbindall", func_unbind); func->set_info("unbind all keys"); func = core::Func::add("view_next", func_view_next); func->set_info("switch to next view"); func = core::Func::add("view_prev", func_view_prev); func->set_info("switch to previous view"); func = core::Func::add("screenshot", func_screenshot); func->set_info("make a screenshot"); keyboard->load_binds(); } void shutdown() { con_print << "^BShutting down input..." << std::endl; Joystick::shutdown(); core::Func::remove("list_actions"); core::Func::remove("list_keys"); core::Func::remove("list_binds"); core::Func::remove("bind"); core::Func::remove("unbind"); core::Func::remove("unbindall"); core::Func::remove("screenshot"); core::Func::remove("ui_control"); core::Func::remove("ui_view"); if (keyboard) { keyboard->save_binds(); delete keyboard; keyboard = 0; } SDL_ShowCursor(SDL_ENABLE); SDL_WM_GrabInput(SDL_GRAB_OFF); // SDL_DisableUNICODE(0); } void action_press(Key *key) { switch(key->action()->id()) { case Action::None: case Action::Console: return; break; /* -- directional control ------------------------- */ case Action::Left: local_direction = 1.0f; break; case Action::Right: local_direction = -1.0f; break; case Action::Up: local_pitch = 1.0f; break; case Action::Down: local_pitch = -1.0f; break; case Action::RollLeft: local_roll = 1.0f; break; case Action::RollRight: local_roll = -1.0f; break; case Action::StrafeLeft: local_strafe = 1.0f; break; case Action::StrafeRight: local_strafe = -1.0f; break; case Action::StrafeUp: local_vstrafe = 1.0f; break; case Action::StrafeDown: local_vstrafe = -1.0f; break; /* -- thruster ------------------------------------ */ case Action::ThrustUp: local_thrust += thruster_offset; break; case Action::ThrustDown: local_thrust -= 2.0f * thruster_offset; break; case Action::Afterburner: local_afterburner = 1.0f; break; case Action::Reverse: local_afterburner = -1.0f; break; /* -- mouse control ------------------------------- */ case Action::Control: if (!mouse_control_override) { mouse_control_override = true; mouse_control_override_time = key->pressed(); } break; /* -- camera control ------------------------------ */ case Action::CamLeft: render::Camera::set_direction(math::min(key->pressed() - core::application()->time(), 1.0f)); break; case Action::CamRight: render::Camera::set_direction(-math::min(key->pressed() - core::application()->time(), 1.0f)); break; case Action::CamUp: render::Camera::set_pitch(math::min(key->pressed() - core::application()->time(), 1.0f)); break; case Action::CamDown: render::Camera::set_pitch(-math::min(key->pressed() - core::application()->time(), 1.0f)); break; case Action::ZoomIn: render::Camera::set_zoom(-0.1f); break; case Action::ZoomOut: render::Camera::set_zoom(+0.1f); break; /* -- fire control -------------------------------- */ case Action::Fire: local_controlflags = local_controlflags | core::EntityControlable::ControlFlagFire; break; } } void action_release(Key *key) { switch(key->action()->id()) { case Action::None: case Action::Console: return; break; /* -- directional control ------------------------- */ case Action::Left: local_direction = 0.0f; break; case Action::Right: local_direction = 0.0f; break; case Action::Up: local_pitch = 0.0f; break; case Action::Down: local_pitch = 0.0f; break; case Action::RollLeft: local_roll = 0.0f; break; case Action::RollRight: local_roll = 0.0f; break; case Action::StrafeLeft: local_strafe = 0.0f; break; case Action::StrafeRight: local_strafe = 0.0f; break; case Action::StrafeUp: local_vstrafe = 0.0f; break; case Action::StrafeDown: local_vstrafe = 0.0f; break; /* -- thruster ------------------------------------ */ case Action::ThrustUp: //local_thrust += 0; break; case Action::ThrustDown: //local_thrust += 0; break; case Action::Afterburner: local_afterburner = 0.0f; break; case Action::Reverse: local_afterburner = 0.0f; break; /* -- mouse control ------------------------------- */ case Action::Control: if (mouse_control_override) { mouse_control_override = false; mouse_control_override_time = 0; if (!input_mousecontrol->value() || (joystick_control && mouse_control && (render::Camera::mode() == render::Camera::Track || render::Camera::mode() == render::Camera::Cockpit))) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; local_vstrafe = 0.0f; local_strafe = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); } } break; /* -- camera control ------------------------------ */ case Action::CamLeft: render::Camera::set_direction(0.0f); break; case Action::CamRight: render::Camera::set_direction(0.0f); break; case Action::CamUp: render::Camera::set_pitch(0); break; case Action::CamDown: render::Camera::set_pitch(0); break; case Action::ZoomIn: break; case Action::ZoomOut: break; /* -- fire control -------------------------------- */ case Action::Fire: local_controlflags = local_controlflags & ~core::EntityControlable::ControlFlagFire; break; } } Key::Modifier modifier() { if ((keyboard_modifiers & Key::Shift) > 0) return Key::Shift; else if ((keyboard_modifiers & Key::Ctrl) > 0) return Key::Ctrl; else if ((keyboard_modifiers & Key::Alt) > 0) return Key::Alt; else return Key::None; } void key_pressed(Key *key) { if (key->action() && (key->action()->id() == Action::Console)) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); ui::console()->toggle(); return; } else if (ui::root()->input_key(true, Keyboard::translate_keysym(key->sym(), keyboard_modifiers), keyboard_modifiers)) { return; } else if (key->bind(modifier()).size() && core::application()->connected()) { if (!core::localplayer()->view() && core::localcontrol()) { if (key->action()) { action_press(key); return; } const char c = key->bind(modifier()).c_str()[0]; if (c == '@') { // target bind if (targets::current_id()) { core::cmd() << key->bind(modifier()) << " " << targets::current_id() << "\n"; } return; } else if (c) { // normal bind core::cmd() << key->bind(modifier()) << "\n"; return; } } else { const char c = key->bind(modifier()).c_str()[0]; if (c && c != '+' && c != '@') { // normal bind core::cmd() << key->bind(modifier()) << "\n"; return; } } } } void key_released(Key *key) { ui::root()->input_key(false, Keyboard::translate_keysym(key->sym(), keyboard_modifiers), keyboard_modifiers); if (core::application()->connected() && core::localcontrol()) { // FIXME mouse release selection should be handled inside the hud // note: mouse button can double as an action key if ((key->sym() == 512 + SDL_BUTTON_LEFT) && targets::hover() && (key->waspressed() <= (input_mousedelay->value() / 1000.0f))) { // hovering target selected targets::set_target(targets::hover()); } if (key->action()) { action_release(key); } } } void axis_event(int axis, int value) { float axis_direction = 0; if (Joystick::input_axisdirection) { axis_direction = truncf(Joystick::input_axisdirection->value()); } float axis_pitch = 0; if (Joystick::input_axispitch) { axis_pitch = truncf(Joystick::input_axispitch->value()); } float axis_roll = 0; if (Joystick::input_axisroll) { axis_roll = truncf(Joystick::input_axisroll->value()); } float axis_throttle = 0; if (Joystick::input_axisthrottle) { axis_throttle = (int) truncf(Joystick::input_axisthrottle->value()); } // value is in range -32768 to 32767 if (axis + 1 == math::absf(axis_direction)) { if (value >= 0) { local_direction = (float) value / 32767.0f; } else { local_direction = (float) value / 32768.0f; } local_direction *= math::sgnf(axis_direction) * -1.0f; } else if (axis + 1 == math::absf(axis_pitch)) { if (value >= 0) { local_pitch = (float) value / 32767.0f; } else { local_pitch = (float) value / 32768.0f; } local_pitch *= math::sgnf(axis_pitch); } else if (axis + 1 == math::absf(axis_roll)) { if (value >= 0) { local_roll = (float) value / 32767.0f; } else { local_roll = (float) value / 32768.0f; } local_roll *= math::sgnf(axis_roll) * -1.0f; } else if (axis + 1 == math::absf(axis_throttle)) { if (value >= 0) { local_thrust = (float) value / 32767.0f; } else { local_thrust = (float) value / 32768.0f; } local_thrust = -0.5f * local_thrust + 0.5f; local_thrust *= math::sgnf(axis_throttle); } } void reset() { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; local_vstrafe = 0.0f; local_strafe = 0.0f; local_afterburner = 0.0f; local_controlflags = core::EntityControlable::ControlFlagNone; if (core::localcontrol()) { local_thrust = core::localcontrol()->thrust(); last_control = core::localcontrol()->id(); } else { local_thrust = 0; last_control = 0; } mouse_pitch = 0.0f; mouse_direction = 0.0f; mouse_x = render::State::width() / 2; mouse_y = render::State::height() / 2; render::Camera::reset(); render::Dust::reset(); mouse_control_override = false; mouse_control_override_time = 0; targets::reset(); for (Keyboard::iterator it = keyboard->begin(); it != keyboard->end(); it++) { Key *key = (*it).second; if (key) { key->key_pressed = 0; key->key_lastpressed = 0; key->key_waspressed = 0; } } last_key = 0; mouse_lastmoved = 0; joystick_lastmoved = 0; keyboard_modifiers = 0; } void frame() { /* -- detect localcontrol() changes --------------- */ if (core::localcontrol() && (last_control != core::localcontrol()->id())) { reset(); } if (core::localcontrol() && (core::localcontrol()->state() != core::Entity::Normal)) { local_thrust = core::localcontrol()->thrust(); } /* -- detect joystick stat changes ---------------- */ Joystick::frame(); joystick_control = Joystick::is_enabled(); /* -- poll SDL keyboard events -------------------- */ SDL_Event event; Key *key = 0; bool pressed = false; bool mouse_moved = false; if (last_key_time + 1.0f < client()->time()) { last_key = 0; last_key_time = 0; } memset(&event, 0, sizeof(SDL_Event)); while (SDL_PollEvent(&event)) { pressed = false; key = 0; switch (event.type) { case SDL_VIDEORESIZE: video::resize(event.resize.w, event.resize.h); break; case SDL_MOUSEMOTION: mouse_x = event.motion.x; mouse_y = event.motion.y; mouse_moved = true; mouse_lastmoved = client()->time(); break; case SDL_MOUSEBUTTONDOWN: key = keyboard->press(512 + event.button.button); pressed = true; break; case SDL_MOUSEBUTTONUP: key = keyboard->release(512 + event.button.button); pressed = false; break; case SDL_JOYBUTTONDOWN: key = keyboard->press(564 + event.jbutton.button); pressed = true; break; case SDL_JOYBUTTONUP: key = keyboard->release(564 + event.jbutton.button); pressed = false; break; case SDL_JOYAXISMOTION: axis_event((int) event.jaxis.axis, (int) event.jaxis.value); joystick_lastmoved = client()->time(); break; case SDL_KEYDOWN: keyboard_modifiers = event.key.keysym.mod; key = keyboard->press(event.key.keysym.sym); pressed = true; break; case SDL_KEYUP: keyboard_modifiers = event.key.keysym.mod; key = keyboard->release(event.key.keysym.sym); pressed = false; break; case SDL_QUIT: core::application()->shutdown(); return; break; } if (key) { if (pressed) { key_pressed(key); last_key = key; // remember the last key that was pressed last_key_time = client()->time(); } else { key_released(key); } } } if (mouse_moved) ui::root()->input_mouse((float) mouse_x, (float) mouse_y); /* -- handle key repeat --------------------------- */ float delay = 200.0f; // key delay time-out in milliseconds if (input_keydelay) { delay = input_keydelay->value(); math::clamp(delay, 50.0f, 500.0f); } float repeat = 35.0f; // key repeat time-out in milliseconds if (input_keyrepeat) { repeat = input_keyrepeat->value(); math::clamp(repeat, 10.0f, 250.0f); } if (input_keydelay) (*input_keydelay) = delay; if (input_keyrepeat) (*input_keyrepeat) = repeat; repeat /= 1000.0f; delay /= 1000.0f; for (Keyboard::iterator it = keyboard->begin(); it != keyboard->end(); it++) { key = (*it).second; if (key && (key->sym() < 512) && key->pressed()) { if ((key->pressed() + delay < core::application()->time()) && (key->lastpressed() + repeat < core::application()->time())) { key->key_lastpressed = core::application()->time(); key_pressed(key); } } } /* -- process mouse movement ----------------------*/ mouse_control = false; if (core::application()->connected() && core::localcontrol()) { mouse_control = ui::console()->hidden() && ((input_mousecontrol->value() > 0) || (mouse_control_override && (mouse_control_override_time + (input_mousedelay->value() / 1000.0f) < core::application()->time()))); if (mouse_control && joystick_control && ((render::Camera::mode() == render::Camera::Track) || (render::Camera::mode() == render::Camera::Cockpit))) { if (!(mouse_control_override && (mouse_control_override_time + (input_mousedelay->value() / 1000.0f) < core::application()->time()))) { mouse_control = false; } } if (mouse_control) { // direction int l = mouse_x - (render::State::width() / 2); mouse_direction = float(-l) / (float)(render::State::width() / 2); // pitch int h = mouse_y - (render::State::height() / 2); mouse_pitch = float(-h) / (float)(render::State::height() / 2); if ((render::Camera::mode() == render::Camera::Track) || (render::Camera::mode() == render::Camera::Cockpit)) { //local_direction = mouse_direction * math::absf(mouse_direction); //local_pitch = mouse_pitch * math::absf(mouse_pitch); local_direction = mouse_direction; local_pitch = mouse_pitch; } else if (render::Camera::mode() == render::Camera::Free) { // squared values to smoothen camera movement render::Camera::set_direction(-mouse_direction * math::absf(mouse_direction)); render::Camera::set_pitch(-mouse_pitch * math::absf(mouse_pitch)); } } math::clamp(local_direction, -1.0f, 1.0f); math::clamp(local_pitch, -1.0f, 1.0f); math::clamp(local_roll, -1.0f, 1.0f); math::clamp(local_thrust, 0.0f, 1.0f); if (local_thrust < 0.01f) local_thrust = 0.0f; else if (local_thrust > 0.99f) local_thrust = 1.0f; core::localcontrol()->set_target_thrust(local_thrust); core::localcontrol()->set_target_direction(local_direction); core::localcontrol()->set_target_pitch(local_pitch / render::State::aspect()); core::localcontrol()->set_target_roll(local_roll); core::localcontrol()->set_target_strafe(local_strafe); core::localcontrol()->set_target_vstrafe(local_vstrafe); core::localcontrol()->set_target_afterburner(local_afterburner); core::localcontrol()->set_target_controlflags(local_controlflags); } else { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; local_vstrafe = 0.0f; local_strafe = 0.0f; local_afterburner = 0.0f; local_controlflags = 0; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); } } } // namespace input } // namespace client