Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands3/src/libraries/core/input/InputManager.cc @ 7201

Last change on this file since 7201 was 7174, checked in by rgrieder, 14 years ago

Only catch exceptions you actually expect. And rethrow unknown exceptions ("…" can also catch internal Microsoft exceptions like floating point exception).

  • Property svn:eol-style set to native
File size: 23.4 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/Clock.h"
44#include "util/Convert.h"
45#include "util/Exception.h"
46#include "util/ScopeGuard.h"
47#include "core/CoreIncludes.h"
48#include "core/ConfigValueIncludes.h"
49#include "core/ConsoleCommand.h"
50#include "core/CommandLineParser.h"
51#include "core/Functor.h"
52#include "core/GraphicsManager.h"
53
54#include "InputBuffer.h"
55#include "JoyStick.h"
56#include "JoyStickQuantityListener.h"
57#include "Mouse.h"
58#include "Keyboard.h"
59
60namespace orxonox
61{
62    SetCommandLineSwitch(keyboard_no_grab).information("Whether not to exclusively grab the keyboard");
63
64    // Abuse of this source file for the InputHandler
65    InputHandler InputHandler::EMPTY;
66
67    InputManager* InputManager::singletonPtr_s = 0;
68
69    //! Defines the |= operator for easier use.
70    inline InputManager::State operator|=(InputManager::State& lval, InputManager::State rval)
71    {
72        return (lval = (InputManager::State)(lval | rval));
73    }
74
75    //! Defines the &= operator for easier use.
76    inline InputManager::State operator&=(InputManager::State& lval, int rval)
77    {
78        return (lval = (InputManager::State)(lval & rval));
79    }
80
81    // ############################################################
82    // #####                  Initialisation                  #####
83    // ##########                                        ##########
84    // ############################################################
85    InputManager::InputManager()
86        : internalState_(Bad)
87        , oisInputManager_(0)
88        , devices_(2)
89        , exclusiveMouse_(TriBool::False)
90        , emptyState_(0)
91        , calibratorCallbackHandler_(0)
92    {
93        RegisterRootObject(InputManager);
94
95        CCOUT(4) << "Constructing..." << std::endl;
96
97        // Allocate space for the function call buffer
98        this->callBuffer_.reserve(16);
99
100        this->setConfigValues();
101
102        if (GraphicsManager::getInstance().isFullScreen())
103            exclusiveMouse_ = TriBool::True;
104        this->loadDevices();
105
106        // Lowest priority empty InputState
107        emptyState_ = createInputState("empty", false, false, InputStatePriority::Empty);
108        emptyState_->setHandler(&InputHandler::EMPTY);
109        activeStates_[emptyState_->getPriority()] = emptyState_;
110
111        // Joy stick calibration helper callback
112        InputState* calibrator = createInputState("calibrator", false, false, InputStatePriority::Calibrator);
113        calibrator->setHandler(&InputHandler::EMPTY);
114        calibratorCallbackHandler_ = new InputBuffer();
115        calibratorCallbackHandler_->registerListener(this, &InputManager::stopCalibration, '\r', true);
116        calibrator->setKeyHandler(calibratorCallbackHandler_);
117
118        this->updateActiveStates();
119
120        // calibrate console command
121        this->getIdentifier()->addConsoleCommand(createConsoleCommand(createFunctor(&InputManager::calibrate, this), "calibrate"), true);
122        // reload console command
123        this->getIdentifier()->addConsoleCommand(createConsoleCommand(createFunctor(&InputManager::reload, this), "reload"), false);
124
125        CCOUT(4) << "Construction complete." << std::endl;
126        internalState_ = Nothing;
127    }
128
129    void InputManager::setConfigValues()
130    {
131    }
132
133    /**
134    @brief
135        Creates the OIS::InputMananger, the keyboard, the mouse and
136        the joys ticks. If either of the first two fail, this method throws an exception.
137    @param windowWidth
138        The width of the render window
139    @param windowHeight
140        The height of the render window
141    */
142    void InputManager::loadDevices()
143    {
144        CCOUT(4) << "Loading input devices..." << std::endl;
145
146        // When loading the devices they should not already be loaded
147        assert(internalState_ & Bad);
148        assert(devices_[InputDeviceEnumerator::Mouse] == 0);
149        assert(devices_[InputDeviceEnumerator::Keyboard] == 0);
150        assert(devices_.size() == InputDeviceEnumerator::FirstJoyStick);
151
152        // Fill parameter list
153        OIS::ParamList paramList;
154        size_t windowHnd = GraphicsManager::getInstance().getRenderWindowHandle();
155        paramList.insert(std::make_pair("WINDOW", multi_cast<std::string>(windowHnd)));
156#if defined(ORXONOX_PLATFORM_WINDOWS)
157        paramList.insert(std::make_pair("w32_keyboard", "DISCL_NONEXCLUSIVE"));
158        paramList.insert(std::make_pair("w32_keyboard", "DISCL_FOREGROUND"));
159        paramList.insert(std::make_pair("w32_mouse", "DISCL_FOREGROUND"));
160        if (exclusiveMouse_ == TriBool::True || GraphicsManager::getInstance().isFullScreen())
161        {
162            // Disable Windows key plus special keys (like play, stop, next, etc.)
163            paramList.insert(std::make_pair("w32_keyboard", "DISCL_NOWINKEY"));
164            paramList.insert(std::make_pair("w32_mouse", "DISCL_EXCLUSIVE"));
165        }
166        else
167            paramList.insert(std::make_pair("w32_mouse", "DISCL_NONEXCLUSIVE"));
168#elif defined(ORXONOX_PLATFORM_LINUX)
169        // Enabling this is probably a bad idea, but whenever orxonox crashes, the setting stays on
170        // Trouble might be that the Pressed event occurs a bit too often...
171        paramList.insert(std::make_pair("XAutoRepeatOn", "true"));
172
173        if (exclusiveMouse_ == TriBool::True || GraphicsManager::getInstance().isFullScreen())
174        {
175            if (CommandLineParser::getValue("keyboard_no_grab").getBool())
176                paramList.insert(std::make_pair("x11_keyboard_grab", "false"));
177            else
178                paramList.insert(std::make_pair("x11_keyboard_grab", "true"));
179            paramList.insert(std::make_pair("x11_mouse_grab",  "true"));
180            paramList.insert(std::make_pair("x11_mouse_hide", "true"));
181        }
182        else
183        {
184            paramList.insert(std::make_pair("x11_keyboard_grab", "false"));
185            paramList.insert(std::make_pair("x11_mouse_grab",  "false"));
186            paramList.insert(std::make_pair("x11_mouse_hide", "false"));
187        }
188#endif
189
190        try
191        {
192            oisInputManager_ = OIS::InputManager::createInputSystem(paramList);
193            // Exception-safety
194            Loki::ScopeGuard guard = Loki::MakeGuard(OIS::InputManager::destroyInputSystem, oisInputManager_);
195            CCOUT(4) << "Created OIS input manager." << std::endl;
196
197            if (oisInputManager_->getNumberOfDevices(OIS::OISKeyboard) > 0)
198                devices_[InputDeviceEnumerator::Keyboard] = new Keyboard(InputDeviceEnumerator::Keyboard, oisInputManager_);
199            else
200                ThrowException(InitialisationFailed, "InputManager: No keyboard found, cannot proceed!");
201
202            // Successful initialisation
203            guard.Dismiss();
204        }
205        catch (const std::exception& ex)
206        {
207            oisInputManager_ = NULL;
208            internalState_ |= Bad;
209            ThrowException(InitialisationFailed, "Could not initialise the input system: " << ex.what());
210        }
211
212        this->loadMouse();
213        this->loadJoySticks();
214
215        // Reorder states in case some joy sticks were added/removed
216        this->updateActiveStates();
217
218        CCOUT(4) << "Input devices loaded." << std::endl;
219    }
220
221    //! Creates a new orxonox::Mouse
222    void InputManager::loadMouse()
223    {
224        if (oisInputManager_->getNumberOfDevices(OIS::OISMouse) > 0)
225        {
226            try
227            {
228                devices_[InputDeviceEnumerator::Mouse] = new Mouse(InputDeviceEnumerator::Mouse, oisInputManager_);
229            }
230            catch (const std::exception& ex)
231            {
232                CCOUT(2) << "Warning: Failed to create Mouse:" << ex.what() << std::endl
233                         << "Proceeding without mouse support." << std::endl;
234            }
235        }
236        else
237            CCOUT(2) << "Warning: No mouse found! Proceeding without mouse support." << std::endl;
238    }
239
240    //! Creates as many joy sticks as are available.
241    void InputManager::loadJoySticks()
242    {
243        for (int i = 0; i < oisInputManager_->getNumberOfDevices(OIS::OISJoyStick); i++)
244        {
245            try
246            {
247                devices_.push_back(new JoyStick(InputDeviceEnumerator::FirstJoyStick + i, oisInputManager_));
248            }
249            catch (const std::exception& ex)
250            {
251                CCOUT(2) << "Warning: Failed to create joy stick: " << ex.what() << std::endl;
252            }
253        }
254
255        // inform all JoyStick Device Number Listeners
256        std::vector<JoyStick*> joyStickList;
257        for (unsigned int i = InputDeviceEnumerator::FirstJoyStick; i < devices_.size(); ++i)
258            joyStickList.push_back(static_cast<JoyStick*>(devices_[i]));
259        JoyStickQuantityListener::changeJoyStickQuantity(joyStickList);
260    }
261
262    // ############################################################
263    // #####                    Destruction                   #####
264    // ##########                                        ##########
265    // ############################################################
266
267    InputManager::~InputManager()
268    {
269        CCOUT(3) << "Destroying..." << std::endl;
270
271        // Leave all active InputStates (except "empty")
272        while (this->activeStates_.size() > 1)
273            this->leaveState(this->activeStates_.rbegin()->second->getName());
274        this->activeStates_.clear();
275
276        // Destroy calibrator helper handler and state
277        this->destroyState("calibrator");
278        // Destroy KeyDetector and state
279        calibratorCallbackHandler_->destroy();
280        // Destroy the empty InputState
281        this->destroyStateInternal(this->emptyState_);
282
283        // Destroy all user InputStates
284        while (statesByName_.size() > 0)
285            this->destroyStateInternal(statesByName_.rbegin()->second);
286
287        if (!(internalState_ & Bad))
288            this->destroyDevices();
289
290        CCOUT(3) << "Destruction complete." << std::endl;
291    }
292
293    /**
294    @brief
295        Destoys all input devices (joy sticks, mouse, keyboard and OIS::InputManager)
296    @throw
297        Method does not throw
298    */
299    void InputManager::destroyDevices()
300    {
301        CCOUT(4) << "Destroying devices..." << std::endl;
302
303        BOOST_FOREACH(InputDevice*& device, devices_)
304        {
305            if (device == NULL)
306                continue;
307            const std::string& className = device->getClassName();
308            delete device;
309            device = 0;
310            CCOUT(4) << className << " destroyed." << std::endl;
311        }
312        devices_.resize(InputDeviceEnumerator::FirstJoyStick);
313
314        assert(oisInputManager_ != NULL);
315        try
316        {
317            OIS::InputManager::destroyInputSystem(oisInputManager_);
318        }
319        catch (const OIS::Exception& ex)
320        {
321            COUT(1) << "OIS::InputManager destruction failed" << ex.eText << std::endl
322                    << "    Potential resource leak!" << std::endl;
323        }
324        oisInputManager_ = NULL;
325
326        internalState_ |= Bad;
327        CCOUT(4) << "Destroyed devices." << std::endl;
328    }
329
330    // ############################################################
331    // #####                     Reloading                    #####
332    // ##########                                        ##########
333    // ############################################################
334
335    void InputManager::reload()
336    {
337        if (internalState_ & Calibrating)
338            CCOUT(2) << "Warning: Cannot reload input system. Joy sticks are currently being calibrated." << std::endl;
339        else
340            reloadInternal();
341    }
342
343    //! Internal reload method. Destroys the OIS devices and loads them again.
344    void InputManager::reloadInternal()
345    {
346        CCOUT(4) << "Reloading ..." << std::endl;
347
348        this->destroyDevices();
349        this->loadDevices();
350
351        internalState_ &= ~Bad;
352        CCOUT(4) << "Reloading complete." << std::endl;
353    }
354
355    // ############################################################
356    // #####                  Runtime Methods                 #####
357    // ##########                                        ##########
358    // ############################################################
359
360    void InputManager::preUpdate(const Clock& time)
361    {
362        if (internalState_ & Bad)
363            ThrowException(General, "InputManager was not correctly reloaded.");
364
365        // check whether a state has changed its EMPTY situation
366        bool bUpdateRequired = false;
367        for (std::map<int, InputState*>::iterator it = activeStates_.begin(); it != activeStates_.end(); ++it)
368        {
369            if (it->second->hasExpired())
370            {
371                it->second->resetExpiration();
372                bUpdateRequired = true;
373            }
374        }
375        if (bUpdateRequired)
376            updateActiveStates();
377
378        // Capture all the input and collect the function calls
379        // No event gets triggered here yet!
380        BOOST_FOREACH(InputDevice* device, devices_)
381            if (device != NULL)
382                device->update(time);
383
384        // Collect function calls for the update
385        for (unsigned int i = 0; i < activeStatesTicked_.size(); ++i)
386            activeStatesTicked_[i]->update(time.getDeltaTime());
387
388        // Execute all cached function calls in order
389        // Why so complicated? The problem is that an InputHandler could trigger
390        // a reload that would destroy the OIS devices or it could even leave and
391        // then destroy its own InputState. That would of course lead to access
392        // violations.
393        // If we delay the calls, then OIS and and the InputStates are not anymore
394        // in the call stack and can therefore be edited.
395        for (size_t i = 0; i < this->callBuffer_.size(); ++i)
396            this->callBuffer_[i]();
397
398        this->callBuffer_.clear();
399    }
400
401    /**
402    @brief
403        Updates the currently active states (according to activeStates_) for each device.
404        Also, a list of all active states (no duplicates!) is compiled for the general preUpdate().
405    */
406    void InputManager::updateActiveStates()
407    {
408        // Calculate the stack of input states
409        // and assign it to the corresponding device
410        for (unsigned int i = 0; i < devices_.size(); ++i)
411        {
412            if (devices_[i] == NULL)
413                continue;
414            std::vector<InputState*>& states = devices_[i]->getStateListRef();
415            bool occupied = false;
416            states.clear();
417            for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin(); rit != activeStates_.rend(); ++rit)
418            {
419                if (rit->second->isInputDeviceEnabled(i) && (!occupied || rit->second->bAlwaysGetsInput_))
420                {
421                    states.push_back(rit->second);
422                    if (!rit->second->bTransparent_)
423                        occupied = true;
424                }
425            }
426        }
427
428        // See that we only update each InputState once for each device
429        // Using an std::set to avoid duplicates
430        std::set<InputState*> tempSet;
431        for (unsigned int i = 0; i < devices_.size(); ++i)
432            if (devices_[i] != NULL)
433                for (unsigned int iState = 0; iState < devices_[i]->getStateListRef().size(); ++iState)
434                    tempSet.insert(devices_[i]->getStateListRef()[iState]);
435
436        // Copy the content of the std::set back to the actual vector
437        activeStatesTicked_.clear();
438        for (std::set<InputState*>::const_iterator it = tempSet.begin();it != tempSet.end(); ++it)
439            activeStatesTicked_.push_back(*it);
440
441        // Check whether we have to change the mouse mode
442        TriBool::Value requestedMode = TriBool::Dontcare;
443        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
444        if (mouseStates.empty())
445            requestedMode = TriBool::False;
446        else
447            requestedMode = mouseStates.front()->getMouseExclusive();
448        if (requestedMode != TriBool::Dontcare && exclusiveMouse_ != requestedMode)
449        {
450            exclusiveMouse_ = requestedMode;
451            if (!GraphicsManager::getInstance().isFullScreen())
452                this->reloadInternal();
453        }
454    }
455
456    void InputManager::clearBuffers()
457    {
458        BOOST_FOREACH(InputDevice* device, devices_)
459            if (device != NULL)
460                device->clearBuffers();
461    }
462
463    void InputManager::calibrate()
464    {
465        COUT(0) << "Move all joy stick axes fully in all directions." << std::endl
466                << "When done, put the axex in the middle position and press enter." << std::endl;
467
468        BOOST_FOREACH(InputDevice* device, devices_)
469            if (device != NULL)
470                device->startCalibration();
471
472        internalState_ |= Calibrating;
473        enterState("calibrator");
474    }
475
476    //! Tells all devices to stop the calibration and evaluate it. Buffers are being cleared as well!
477    void InputManager::stopCalibration()
478    {
479        BOOST_FOREACH(InputDevice* device, devices_)
480            if (device != NULL)
481                device->stopCalibration();
482
483        // restore old input state
484        leaveState("calibrator");
485        internalState_ &= ~Calibrating;
486        // Clear buffers to prevent button hold events
487        this->clearBuffers();
488
489        COUT(0) << "Calibration has been stored." << std::endl;
490    }
491
492    //! Gets called by WindowEventListener upon focus change --> clear buffers
493    void InputManager::windowFocusChanged()
494    {
495        this->clearBuffers();
496    }
497
498    std::pair<int, int> InputManager::getMousePosition() const
499    {
500        Mouse* mouse = static_cast<Mouse*>(devices_[InputDeviceEnumerator::Mouse]);
501        if (mouse != NULL)
502        {
503            const OIS::MouseState state = mouse->getOISDevice()->getMouseState();
504            return std::make_pair(state.X.abs, state.Y.abs);
505        }
506        else
507            return std::make_pair(0, 0);
508    }
509
510    // ############################################################
511    // #####                    Input States                  #####
512    // ##########                                        ##########
513    // ############################################################
514
515    InputState* InputManager::createInputState(const std::string& name, bool bAlwaysGetsInput, bool bTransparent, InputStatePriority priority)
516    {
517        if (name.empty())
518            return 0;
519        if (statesByName_.find(name) == statesByName_.end())
520        {
521            if (priority >= InputStatePriority::HighPriority || priority == InputStatePriority::Empty)
522            {
523                // Make sure we don't add two high priority states with the same priority
524                for (std::map<std::string, InputState*>::const_iterator it = this->statesByName_.begin();
525                    it != this->statesByName_.end(); ++it)
526                {
527                    if (it->second->getPriority() == priority)
528                    {
529                        COUT(2) << "Warning: Could not add an InputState with the same priority '"
530                            << static_cast<int>(priority) << "' != 0." << std::endl;
531                        return 0;
532                    }
533                }
534            }
535            InputState* state = new InputState(name, bAlwaysGetsInput, bTransparent, priority);
536            statesByName_[name] = state;
537
538            return state;
539        }
540        else
541        {
542            COUT(2) << "Warning: Could not add an InputState with the same name '" << name << "'." << std::endl;
543            return 0;
544        }
545    }
546
547    InputState* InputManager::getState(const std::string& name)
548    {
549        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
550        if (it != statesByName_.end())
551            return it->second;
552        else
553            return 0;
554    }
555
556    bool InputManager::enterState(const std::string& name)
557    {
558        // get pointer from the map with all stored handlers
559        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
560        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) == activeStates_.end())
561        {
562            // exists and not active
563            if (it->second->getPriority() == 0)
564            {
565                // Get smallest possible priority between 1 and maxStateStackSize_s
566                for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
567                    rit != activeStates_.rend(); ++rit)
568                {
569                    if (rit->first < InputStatePriority::HighPriority)
570                    {
571                        it->second->setPriority(rit->first + 1);
572                        break;
573                    }
574                }
575                // In case no normal handler was on the stack
576                if (it->second->getPriority() == 0)
577                    it->second->setPriority(1);
578            }
579            activeStates_[it->second->getPriority()] = it->second;
580            updateActiveStates();
581            it->second->entered();
582
583            return true;
584        }
585        return false;
586    }
587
588    bool InputManager::leaveState(const std::string& name)
589    {
590        if (name == "empty")
591        {
592            COUT(2) << "InputManager: Leaving the empty state is not allowed!" << std::endl;
593            return false;
594        }
595        // get pointer from the map with all stored handlers
596        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
597        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) != activeStates_.end())
598        {
599            // exists and active
600
601            it->second->left();
602
603            activeStates_.erase(it->second->getPriority());
604            if (it->second->getPriority() < InputStatePriority::HighPriority)
605                it->second->setPriority(0);
606            updateActiveStates();
607
608            return true;
609        }
610        return false;
611    }
612
613    bool InputManager::destroyState(const std::string& name)
614    {
615        if (name == "empty")
616        {
617            COUT(2) << "InputManager: Removing the empty state is not allowed!" << std::endl;
618            return false;
619        }
620        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
621        if (it != statesByName_.end())
622        {
623            this->leaveState(name);
624            destroyStateInternal(it->second);
625
626            return true;
627        }
628        return false;
629    }
630
631    //! Destroys an InputState internally.
632    void InputManager::destroyStateInternal(InputState* state)
633    {
634        assert(state && this->activeStates_.find(state->getPriority()) == this->activeStates_.end());
635        statesByName_.erase(state->getName());
636        state->destroy();
637    }
638}
Note: See TracBrowser for help on using the repository browser.