Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

replaced the temporary names of all ConsoleCommand related classes and functions by their real names

  • 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
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    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.