Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7204 was 7204, checked in by landauf, 14 years ago

adjusted includes in all other files

  • 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/CommandLineParser.h"
50#include "core/GraphicsManager.h"
51#include "core/command/ConsoleCommand.h"
52#include "core/command/Functor.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.