// Description: // Input subsystem. // // Copyright (C) 2001 Frank Becker // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 2 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details // #include #include #include #include #include #include #include #include #include #include #include #include "SDL.h" Input::Input( void): _bindMode( false), _callback(0), _callbackManager(), _memoryDX(0.0), _memoryDY(0.0), _valDX(0.0), _valDY(0.0), _dampVal(0.0), _interceptor(0) { XTRACE(); } Input::~Input() { XTRACE(); _callbackMap.clear(); } Input* Input::preinit( void) { _callbackManager.init(); return this; } bool Input::init( void) { XTRACE(); LOG_INFO << "Initializing Input..." << endl; updateMouseSettings(); LOG_INFO << "Mouse smoothing: " << _dampVal << endl; LOG_INFO << "Mouse sensitivity: " << _sensitivity << endl; SDL_EnableKeyRepeat( 300,200); LOG_INFO << "Input OK." << endl; return true; } void Input::updateMouseSettings( void) { ConfigS::instance()->getFloat( "mouseSmooth", _dampVal); if( (_dampVal<0.0) || (_dampVal>0.9)) { _dampVal = 0.0f; } ConfigS::instance()->getFloat( "mouseSensitivity", _sensitivity); if( (_sensitivity<0.01) || (_sensitivity>1.0)) { _sensitivity = 0.1f; } } // Returns false, if there are no more events available. bool Input::tryGetTrigger( Trigger &trigger, bool &isDown) { // XTRACE(); isDown = false; SDL_Event event; if( !SDL_PollEvent( &event)) { return false; } switch( event.type ) { case SDL_KEYDOWN: isDown = true; #ifndef NO_QUICK_EXIT if( event.key.keysym.sym == SDLK_BACKQUOTE) { GameState::isAlive = false; LOG_WARNING << "Quick Exit invoked..." << endl; trigger.type = eUnknownTrigger; break; } #endif //fall through case SDL_KEYUP: trigger.type = eKeyTrigger; trigger.data1 = event.key.keysym.sym; trigger.data2 = event.key.keysym.mod; trigger.data3 = event.key.keysym.unicode; break; case SDL_MOUSEBUTTONDOWN: isDown = true; //fall through case SDL_MOUSEBUTTONUP: trigger.type = eButtonTrigger; trigger.data1 = event.button.button; trigger.data2 = 0; trigger.data3 = 0; break; case SDL_MOUSEMOTION: trigger.type = eUnknownTrigger; _valDX = event.motion.xrel*_sensitivity; _valDY = -event.motion.yrel*_sensitivity; break; case SDL_QUIT: GameState::isAlive = false; break; default: trigger.type = eUnknownTrigger; break; } return true; } bool Input::update( void) { // XTRACE(); bool isDown; Trigger trigger; static float nextTime = Timer::getTime()+0.5f; float thisTime = Timer::getTime(); if( thisTime > nextTime) { updateMouseSettings(); nextTime = thisTime+0.5f; } _valDX = 0.0f; _valDY = 0.0f; while( tryGetTrigger( trigger, isDown)) { if( trigger.type == eUnknownTrigger) { //for unkown trigger we don't need to do a lookup continue; } if( _interceptor) { //feed trigger to interceptor instead of normal callback mechanism _interceptor->input( trigger, isDown); continue; } if( !_bindMode) { //find callback for this trigger //i.e. the action bound to this key Callback * cb = findHash( trigger, _callbackMap); if( cb) { // LOG_INFO << "Callback for [" << cb->getActionName() << "]" << endl; cb->performAction( trigger, isDown); } } else if( _callback && (trigger.type!=eMotionTrigger)) { //Note: motion triggers can't be bound //we are in bind-mode, so bind this trigger to callback bind( trigger, _callback); //go back to normal mode _bindMode = false; } else if( !_callback) { LOG_ERROR << "Input is in bind mode, but callback is 0" << endl; _bindMode = false; } } _valDX = feedbackFilter( _valDX, _dampVal, _memoryDX); _valDY = feedbackFilter( _valDY, _dampVal, _memoryDY); if( (fabs(_valDX)>1.0e-10) || (fabs(_valDY)>1.0e-10)) { trigger.type = eMotionTrigger; trigger.fData1 = _valDX; trigger.fData2 = _valDY; if( _interceptor) { //feed trigger to interceptor instead of normal callback mechanism _interceptor->input( trigger, true); } else { Callback * cb = findHash( trigger, _callbackMap); if( cb) { // LOG_INFO << "Callback for [" << cb->getActionName() << "]" << endl; cb->performAction( trigger, isDown); } } } return true; } void Input::handleLine( const string line) { // XTRACE(); Tokenizer t( line); string bindKeyword = t.next(); if( bindKeyword != "bind") return; string action = t.next(); string keyname = t.next(); // LOG_INFO << "action [" << action << "], " // << "keyname [" << keyname << "]" << endl; Trigger trigger; if( _keys.convertStringToTrigger( keyname, trigger)) { Callback *cb = _callbackManager.getCallback( action); bind( trigger, cb); } } void Input::save( ofstream &outfile) { XTRACE(); outfile << "# --- Binding section --- " << endl; hash_map< Trigger, Callback*, hash >::const_iterator ci; for( ci=_callbackMap.begin(); ci!=_callbackMap.end(); ci++) { outfile << "bind " << ci->second->getActionName() << " " << _keys.convertTriggerToString( ci->first) << endl; } } void Input::bind( Trigger &trigger, Callback *callback) { XTRACE(); Callback * cb = findHash( trigger, _callbackMap); if( cb) { LOG_WARNING << "Removing old binding" << endl; //remove previous callback... _callbackMap.erase( trigger); delete cb; } LOG_INFO << "Creating binding for " << callback->getActionName() << " - " << trigger.type << ":" << trigger.data1 << endl; _callbackMap[ trigger] = callback; }