Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/input/InputManager.cc @ 7284

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

merged consolecommands3 branch back to trunk.

note: the console command interface has changed completely, but the documentation is not yet up to date. just copy an existing command and change it to your needs, it's pretty self-explanatory. also the include files related to console commands are now located in core/command/. in the game it should work exactly like before, except for some changes in the auto-completion.

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