Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core4/src/core/input/InputManager.cc @ 3310

Last change on this file since 3310 was 3310, checked in by rgrieder, 15 years ago

Optimisations in the pathway of the input. Nobody will ever notice the difference in performance (immeasurable), but I love the beauty of having all my template code inlined when it even decreases code size (because the code gets inlined exactly once).

  • Property svn:eol-style set to native
File size: 25.0 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30@file
31@brief
32    Implementation of the InputManager and a static variable from the InputHandler.
33*/
34
35#include "InputManager.h"
36
37#include <cassert>
38#include <climits>
39#include <ois/OISException.h>
40#include <ois/OISInputManager.h>
41#include <boost/foreach.hpp>
42
43#include "util/Exception.h"
44#include "util/ScopeGuard.h"
45#include "core/Clock.h"
46#include "core/CoreIncludes.h"
47#include "core/ConfigValueIncludes.h"
48#include "core/ConsoleCommand.h"
49#include "core/CommandLine.h"
50#include "core/Functor.h"
51
52#include "InputBuffer.h"
53#include "KeyDetector.h"
54#include "JoyStick.h"
55#include "JoyStickQuantityListener.h"
56#include "Mouse.h"
57#include "Keyboard.h"
58
59namespace orxonox
60{
61    SetCommandLineSwitch(keyboard_no_grab).information("Whether not to exclusively grab the keyboard");
62
63    // Abuse of this source file for the InputHandler
64    InputHandler InputHandler::EMPTY;
65
66    InputManager* InputManager::singletonRef_s = 0;
67
68    //! Defines the |= operator for easier use.
69    inline InputManager::State operator|=(InputManager::State& lval, InputManager::State rval)
70    {
71        return (lval = (InputManager::State)(lval | rval));
72    }
73
74    //! Defines the &= operator for easier use.
75    inline InputManager::State operator&=(InputManager::State& lval, int rval)
76    {
77        return (lval = (InputManager::State)(lval & rval));
78    }
79
80    // ############################################################
81    // #####                  Initialisation                  #####
82    // ##########                                        ##########
83    // ############################################################
84    InputManager::InputManager(size_t windowHnd)
85        : internalState_(Bad)
86        , oisInputManager_(0)
87        , devices_(2)
88        , windowHnd_(0)
89        , emptyState_(0)
90        , keyDetector_(0)
91        , calibratorCallbackHandler_(0)
92    {
93        RegisterRootObject(InputManager);
94
95        assert(singletonRef_s == 0);
96        singletonRef_s = this;
97
98        CCOUT(4) << "Constructing..." << std::endl;
99
100        this->setConfigValues();
101
102        this->loadDevices(windowHnd);
103
104        // Lowest priority empty InputState
105        emptyState_ = createInputState("empty", false, false, InputStatePriority::Empty);
106        emptyState_->setHandler(&InputHandler::EMPTY);
107        activeStates_[emptyState_->getPriority()] = emptyState_;
108
109        // KeyDetector to evaluate a pressed key's name
110        InputState* detector = createInputState("detector", false, false, InputStatePriority::Detector);
111        // Create a callback to avoid buttonHeld events after the key has been detected
112        FunctorMember<InputManager>* bufferFunctor = createFunctor(&InputManager::clearBuffers);
113        bufferFunctor->setObject(this);
114        detector->setLeaveFunctor(bufferFunctor);
115        keyDetector_ = new KeyDetector();
116        detector->setHandler(keyDetector_);
117
118        // Joy stick calibration helper callback
119        InputState* calibrator = createInputState("calibrator", false, false, InputStatePriority::Calibrator);
120        calibrator->setHandler(&InputHandler::EMPTY);
121        calibratorCallbackHandler_ = new InputBuffer();
122        calibratorCallbackHandler_->registerListener(this, &InputManager::stopCalibration, '\r', true);
123        calibrator->setKeyHandler(calibratorCallbackHandler_);
124
125        this->updateActiveStates();
126
127        {
128            // calibrate console command
129            FunctorMember<InputManager>* functor = createFunctor(&InputManager::calibrate);
130            functor->setObject(this);
131            this->getIdentifier()->addConsoleCommand(createConsoleCommand(functor, "calibrate"), true);
132        }
133        {
134            // reload console command
135            FunctorMember<InputManager>* functor = createFunctor(&InputManager::reload);
136            functor->setObject(this);
137            this->getIdentifier()->addConsoleCommand(createConsoleCommand(functor, "reload"), false);
138        }
139
140        internalState_ = Nothing;
141        CCOUT(4) << "Construction complete." << std::endl;
142    }
143
144    void InputManager::setConfigValues()
145    {
146    }
147
148    /**
149    @brief
150        Creates the OIS::InputMananger, the keyboard, the mouse and
151        the joys ticks. If either of the first two fail, this method throws an exception.
152    @param windowHnd
153        The window handle of the render window
154    @param windowWidth
155        The width of the render window
156    @param windowHeight
157        The height of the render window
158    */
159    void InputManager::loadDevices(size_t windowHnd)
160    {
161        CCOUT(3) << "Loading input devices..." << std::endl;
162
163        // When loading the devices they should not already be loaded
164        assert(internalState_ & Bad);
165        assert(devices_[InputDeviceEnumerator::Mouse] == 0);
166        assert(devices_[InputDeviceEnumerator::Keyboard] == 0);
167        assert(devices_.size() == InputDeviceEnumerator::FirstJoyStick);
168
169        // store handle internally so we can reload OIS
170        windowHnd_ = windowHnd;
171
172        OIS::ParamList paramList;
173        std::ostringstream windowHndStr;
174
175        // Fill parameter list
176        windowHndStr << (unsigned int)windowHnd_;
177        paramList.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
178#if defined(ORXONOX_PLATFORM_WINDOWS)
179        paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
180        paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND")));
181        paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
182        paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
183#elif defined(ORXONOX_PLATFORM_LINUX)
184        paramList.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true")));
185        paramList.insert(std::make_pair(std::string("x11_mouse_grab"), "true"));
186        paramList.insert(std::make_pair(std::string("x11_mouse_hide"), "true"));
187        bool kbNoGrab;
188        CommandLine::getValue("keyboard_no_grab", &kbNoGrab);
189        if (kbNoGrab)
190            paramList.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
191        else
192            paramList.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("true")));
193#endif
194
195        try
196        {
197            oisInputManager_ = OIS::InputManager::createInputSystem(paramList);
198            // Exception-safety
199            Loki::ScopeGuard guard = Loki::MakeGuard(OIS::InputManager::destroyInputSystem, oisInputManager_);
200            CCOUT(ORX_DEBUG) << "Created OIS input manager." << std::endl;
201
202            if (oisInputManager_->getNumberOfDevices(OIS::OISKeyboard) > 0)
203                devices_[InputDeviceEnumerator::Keyboard] = new Keyboard(InputDeviceEnumerator::Keyboard, oisInputManager_);
204            else
205                ThrowException(InitialisationFailed, "InputManager: No keyboard found, cannot proceed!");
206
207            // Successful initialisation
208            guard.Dismiss();
209        }
210        catch (std::exception& ex)
211        {
212            oisInputManager_ = NULL;
213            internalState_ |= Bad;
214            ThrowException(InitialisationFailed, "Could not initialise the input system: " << ex.what());
215        }
216
217        // TODO: Remove the two parameters
218        this->loadMouse();
219        this->loadJoySticks();
220
221        // Reorder states in case some joy sticks were added/removed
222        this->updateActiveStates();
223
224        CCOUT(3) << "Input devices loaded." << std::endl;
225    }
226
227    //! Creates a new orxonox::Mouse
228    void InputManager::loadMouse()
229    {
230        if (oisInputManager_->getNumberOfDevices(OIS::OISMouse) > 0)
231        {
232            try
233            {
234                devices_[InputDeviceEnumerator::Mouse] = new Mouse(InputDeviceEnumerator::Mouse, oisInputManager_);
235            }
236            catch (const OIS::Exception& ex)
237            {
238                CCOUT(2) << "Warning: Failed to create Mouse:" << ex.eText << std::endl
239                         << "Proceeding without mouse support." << std::endl;
240            }
241        }
242        else
243            CCOUT(ORX_WARNING) << "Warning: No mouse found! Proceeding without mouse support." << std::endl;
244    }
245
246    //! Creates as many joy sticks as are available.
247    void InputManager::loadJoySticks()
248    {
249        for (int i = 0; i < oisInputManager_->getNumberOfDevices(OIS::OISJoyStick); i++)
250        {
251            try
252            {
253                devices_.push_back(new JoyStick(InputDeviceEnumerator::FirstJoyStick + i, oisInputManager_));
254            }
255            catch (std::exception ex)
256            {
257                CCOUT(2) << "Warning: Failed to create joy stick: " << ex.what() << std::endl;
258            }
259        }
260
261        // inform all JoyStick Device Number Listeners
262        std::vector<JoyStick*> joyStickList;
263        for (unsigned int i = InputDeviceEnumerator::FirstJoyStick; i < devices_.size(); ++i)
264            joyStickList.push_back(static_cast<JoyStick*>(devices_[i]));
265        JoyStickQuantityListener::changeJoyStickQuantity(joyStickList);
266    }
267
268    void InputManager::setKeyDetectorCallback(const std::string& command)
269    {
270        this->keyDetector_->setCallbackCommand(command);
271    }
272
273    // ############################################################
274    // #####                    Destruction                   #####
275    // ##########                                        ##########
276    // ############################################################
277
278    InputManager::~InputManager()
279    {
280        CCOUT(4) << "Destroying..." << std::endl;
281
282        // Destroy calibrator helper handler and state
283        delete keyDetector_;
284        this->destroyState("calibrator");
285        // Destroy KeyDetector and state
286        delete calibratorCallbackHandler_;
287        this->destroyState("detector");
288        // destroy the empty InputState
289        this->destroyStateInternal(this->emptyState_);
290
291        // destroy all user InputStates
292        while (statesByName_.size() > 0)
293            this->destroyStateInternal((*statesByName_.rbegin()).second);
294
295        if (!(internalState_ & Bad))
296            this->destroyDevices();
297
298        CCOUT(4) << "Destruction complete." << std::endl;
299        singletonRef_s = 0;
300    }
301
302    /**
303    @brief
304        Destoys all input devices (joy sticks, mouse, keyboard and OIS::InputManager)
305    @throw
306        Method does not throw
307    */
308    void InputManager::destroyDevices()
309    {
310        CCOUT(3) << "Destroying devices..." << std::endl;
311
312        BOOST_FOREACH(InputDevice*& device, devices_)
313        {
314            if (device == NULL)
315                continue;
316            std::string className = device->getClassName();
317            try
318            {
319                delete device;
320                device = 0;
321                CCOUT(4) << className << " destroyed." << std::endl;
322            }
323            catch (...)
324            {
325                CCOUT(1) << className << " destruction failed! Potential resource leak!" << std::endl;
326            }
327        }
328        devices_.resize(InputDeviceEnumerator::FirstJoyStick);
329
330        assert(oisInputManager_ != NULL);
331        try
332        {
333            OIS::InputManager::destroyInputSystem(oisInputManager_);
334        }
335        catch (...)
336        {
337            CCOUT(1) << "OIS::InputManager destruction failed! Potential resource leak!" << std::endl;
338        }
339        oisInputManager_ = NULL;
340
341        internalState_ |= Bad;
342        CCOUT(3) << "Destroyed devices." << std::endl;
343    }
344
345    // ############################################################
346    // #####                     Reloading                    #####
347    // ##########                                        ##########
348    // ############################################################
349
350    void InputManager::reload()
351    {
352        if (internalState_ & Ticking)
353        {
354            // We cannot destroy OIS right now, because reload was probably
355            // caused by a user clicking on a GUI item. The stack trace would then
356            // include an OIS method. So it would be a very bad thing to destroy it..
357            internalState_ |= ReloadRequest;
358        }
359        else if (internalState_ & Calibrating)
360            CCOUT(2) << "Warning: Cannot reload input system. Joy sticks are currently being calibrated." << std::endl;
361        else
362            reloadInternal();
363    }
364
365    //! Internal reload method. Destroys the OIS devices and loads them again.
366    void InputManager::reloadInternal()
367    {
368        CCOUT(3) << "Reloading ..." << std::endl;
369
370        this->destroyDevices();
371        this->loadDevices(windowHnd_);
372
373        internalState_ &= ~Bad;
374        internalState_ &= ~ReloadRequest;
375        CCOUT(3) << "Reloading complete." << std::endl;
376    }
377
378    // ############################################################
379    // #####                  Runtime Methods                 #####
380    // ##########                                        ##########
381    // ############################################################
382
383    void InputManager::update(const Clock& time)
384    {
385        if (internalState_ & Bad)
386            ThrowException(General, "InputManager was not correctly reloaded.");
387
388        else if (internalState_ & ReloadRequest)
389            reloadInternal();
390
391        // check for states to leave
392        if (!stateLeaveRequests_.empty())
393        {
394            for (std::set<InputState*>::iterator it = stateLeaveRequests_.begin();
395                it != stateLeaveRequests_.end(); ++it)
396            {
397                (*it)->left();
398                // just to be sure that the state actually is registered
399                assert(statesByName_.find((*it)->getName()) != statesByName_.end());
400
401                activeStates_.erase((*it)->getPriority());
402                if ((*it)->getPriority() < InputStatePriority::HighPriority)
403                    (*it)->setPriority(0);
404                updateActiveStates();
405            }
406            stateLeaveRequests_.clear();
407        }
408
409        // check for states to enter
410        if (!stateEnterRequests_.empty())
411        {
412            for (std::set<InputState*>::const_iterator it = stateEnterRequests_.begin();
413                it != stateEnterRequests_.end(); ++it)
414            {
415                // just to be sure that the state actually is registered
416                assert(statesByName_.find((*it)->getName()) != statesByName_.end());
417
418                if ((*it)->getPriority() == 0)
419                {
420                    // Get smallest possible priority between 1 and maxStateStackSize_s
421                    for(std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
422                        rit != activeStates_.rend(); ++rit)
423                    {
424                        if (rit->first < InputStatePriority::HighPriority)
425                        {
426                            (*it)->setPriority(rit->first + 1);
427                            break;
428                        }
429                    }
430                    // In case no normal handler was on the stack
431                    if ((*it)->getPriority() == 0)
432                        (*it)->setPriority(1);
433                }
434                activeStates_[(*it)->getPriority()] = (*it);
435                updateActiveStates();
436                (*it)->entered();
437            }
438            stateEnterRequests_.clear();
439        }
440
441        // check for states to destroy
442        if (!stateDestroyRequests_.empty())
443        {
444            for (std::set<InputState*>::iterator it = stateDestroyRequests_.begin();
445                it != stateDestroyRequests_.end(); ++it)
446            {
447                destroyStateInternal((*it));
448            }
449            stateDestroyRequests_.clear();
450        }
451
452        // check whether a state has changed its EMPTY situation
453        bool bUpdateRequired = false;
454        for (std::map<int, InputState*>::iterator it = activeStates_.begin(); it != activeStates_.end(); ++it)
455        {
456            if (it->second->hasExpired())
457            {
458                it->second->resetExpiration();
459                bUpdateRequired = true;
460            }
461        }
462        if (bUpdateRequired)
463            updateActiveStates();
464
465        // mark that we now start capturing and distributing input
466        internalState_ |= Ticking;
467
468        // Capture all the input and handle it
469        BOOST_FOREACH(InputDevice* device, devices_)
470            if (device != NULL)
471                device->update(time);
472
473        // Update the states
474        for (unsigned int i = 0; i < activeStatesTicked_.size(); ++i)
475            activeStatesTicked_[i]->update(time.getDeltaTime());
476
477        internalState_ &= ~Ticking;
478    }
479
480    /**
481    @brief
482        Updates the currently active states (according to activeStates_) for each device.
483        Also, a list of all active states (no duplicates!) is compiled for the general update().
484    */
485    void InputManager::updateActiveStates()
486    {
487        // temporary resize
488        for (unsigned int i = 0; i < devices_.size(); ++i)
489        {
490            if (devices_[i] == NULL)
491                continue;
492            std::vector<InputState*>& states = devices_[i]->getStateListRef();
493            bool occupied = false;
494            states.clear();
495            for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin(); rit != activeStates_.rend(); ++rit)
496            {
497                if (rit->second->isInputDeviceEnabled(i) && (!occupied || rit->second->bAlwaysGetsInput_))
498                {
499                    states.push_back(rit->second);
500                    if (!rit->second->bTransparent_)
501                        occupied = true;
502                }
503            }
504        }
505
506        // update tickables (every state will only appear once)
507        // Using a std::set to avoid duplicates
508        std::set<InputState*> tempSet;
509        for (unsigned int i = 0; i < devices_.size(); ++i)
510            if (devices_[i] != NULL)
511                for (unsigned int iState = 0; iState < devices_[i]->getStateListRef().size(); ++iState)
512                    tempSet.insert(devices_[i]->getStateListRef()[iState]);
513
514        // copy the content of the std::set back to the actual vector
515        activeStatesTicked_.clear();
516        for (std::set<InputState*>::const_iterator it = tempSet.begin();it != tempSet.end(); ++it)
517            activeStatesTicked_.push_back(*it);
518    }
519
520    void InputManager::clearBuffers()
521    {
522        BOOST_FOREACH(InputDevice* device, devices_)
523            if (device != NULL)
524                device->clearBuffers();
525    }
526
527    void InputManager::calibrate()
528    {
529        COUT(0) << "Move all joy stick axes fully in all directions." << std::endl
530                << "When done, put the axex in the middle position and press enter." << std::endl;
531
532        BOOST_FOREACH(InputDevice* device, devices_)
533            if (device != NULL)
534                device->startCalibration();
535
536        internalState_ |= Calibrating;
537        enterState("calibrator");
538    }
539
540    //! Tells all devices to stop the calibration and evaluate it. Buffers are being cleared as well!
541    void InputManager::stopCalibration()
542    {
543        BOOST_FOREACH(InputDevice* device, devices_)
544            if (device != NULL)
545                device->stopCalibration();
546
547        // restore old input state
548        leaveState("calibrator");
549        internalState_ &= ~Calibrating;
550        // Clear buffers to prevent button hold events
551        this->clearBuffers();
552
553        COUT(0) << "Calibration has been stored." << std::endl;
554    }
555
556    //! Gets called by WindowEventListener upon focus change --> clear buffers
557    void InputManager::windowFocusChanged()
558    {
559        this->clearBuffers();
560    }
561
562    // ############################################################
563    // #####                    Iput States                   #####
564    // ##########                                        ##########
565    // ############################################################
566
567    InputState* InputManager::createInputState(const std::string& name, bool bAlwaysGetsInput, bool bTransparent, InputStatePriority priority)
568    {
569        if (name == "")
570            return 0;
571        if (statesByName_.find(name) == statesByName_.end())
572        {
573            if (priority >= InputStatePriority::HighPriority || priority == InputStatePriority::Empty)
574            {
575                // Make sure we don't add two high priority states with the same priority
576                for (std::map<std::string, InputState*>::const_iterator it = this->statesByName_.begin();
577                    it != this->statesByName_.end(); ++it)
578                {
579                    if (it->second->getPriority() == priority)
580                    {
581                        COUT(2) << "Warning: Could not add an InputState with the same priority '"
582                            << static_cast<int>(priority) << "' != 0." << std::endl;
583                        return 0;
584                    }
585                }
586            }
587            InputState* state = new InputState(name, bAlwaysGetsInput, bTransparent, priority);
588            statesByName_[name] = state;
589
590            return state;
591        }
592        else
593        {
594            COUT(2) << "Warning: Could not add an InputState with the same name '" << name << "'." << std::endl;
595            return 0;
596        }
597    }
598
599    InputState* InputManager::getState(const std::string& name)
600    {
601        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
602        if (it != statesByName_.end())
603            return it->second;
604        else
605            return 0;
606    }
607
608    bool InputManager::enterState(const std::string& name)
609    {
610        // get pointer from the map with all stored handlers
611        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
612        if (it != statesByName_.end())
613        {
614            // exists
615            if (activeStates_.find(it->second->getPriority()) == activeStates_.end())
616            {
617                // not active
618                if (stateDestroyRequests_.find(it->second) == stateDestroyRequests_.end())
619                {
620                    // not scheduled for destruction
621                    // prevents a state being added multiple times
622                    stateEnterRequests_.insert(it->second);
623                    return true;
624                }
625            }
626        }
627        return false;
628    }
629
630    bool InputManager::leaveState(const std::string& name)
631    {
632        if (name == "empty")
633        {
634            COUT(2) << "InputManager: Leaving the empty state is not allowed!" << std::endl;
635            return false;
636        }
637        // get pointer from the map with all stored handlers
638        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
639        if (it != statesByName_.end())
640        {
641            // exists
642            if (activeStates_.find(it->second->getPriority()) != activeStates_.end())
643            {
644                // active
645                stateLeaveRequests_.insert(it->second);
646                return true;
647            }
648        }
649        return false;
650    }
651
652    bool InputManager::destroyState(const std::string& name)
653    {
654        if (name == "empty")
655        {
656            COUT(2) << "InputManager: Removing the empty state is not allowed!" << std::endl;
657            return false;
658        }
659        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
660        if (it != statesByName_.end())
661        {
662            if (activeStates_.find(it->second->getPriority()) != activeStates_.end())
663            {
664                // The state is still active. We have to postpone
665                stateLeaveRequests_.insert(it->second);
666                stateDestroyRequests_.insert(it->second);
667            }
668            else if (this->internalState_ & Ticking)
669            {
670                // cannot remove state while ticking
671                stateDestroyRequests_.insert(it->second);
672            }
673            else
674                destroyStateInternal(it->second);
675
676            return true;
677        }
678        return false;
679    }
680
681    //! Destroys an InputState internally.
682    void InputManager::destroyStateInternal(InputState* state)
683    {
684        assert(state && !(this->internalState_ & Ticking));
685        std::map<int, InputState*>::iterator it = this->activeStates_.find(state->getPriority());
686        if (it != this->activeStates_.end())
687        {
688            this->activeStates_.erase(it);
689            updateActiveStates();
690        }
691        statesByName_.erase(state->getName());
692        delete state;
693    }
694}
Note: See TracBrowser for help on using the repository browser.