Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gamestate/src/libraries/core/input/InputManager.cc @ 6658

Last change on this file since 6658 was 6657, checked in by rgrieder, 14 years ago

Implemented indirect function calls for input events. It simply means that when you hit the mouse button, the resulting call stack will not include OIS nor an InputState anymore.
—> You can reload the InputManager (happens when mouse mode changes) or even modify the InputState situation (like InputManager::leaveState or even destroyState) directly now.
—> I was able to remove the indirect execution of following methods in the InputManager: leaveInputState, enterInputState, destroyInputState and reload.

  • Property svn:eol-style set to native
File size: 23.6 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            try
309            {
310                delete device;
311                device = 0;
312                CCOUT(4) << className << " destroyed." << std::endl;
313            }
314            catch (...)
315            {
316                COUT(1) << className << " destruction failed: " << Exception::handleMessage() << std::endl
317                        << "    Potential resource leak!" << std::endl;
318            }
319        }
320        devices_.resize(InputDeviceEnumerator::FirstJoyStick);
321
322        assert(oisInputManager_ != NULL);
323        try
324        {
325            OIS::InputManager::destroyInputSystem(oisInputManager_);
326        }
327        catch (...)
328        {
329            COUT(1) << "OIS::InputManager destruction failed" << Exception::handleMessage() << std::endl
330                    << "    Potential resource leak!" << std::endl;
331        }
332        oisInputManager_ = NULL;
333
334        internalState_ |= Bad;
335        CCOUT(4) << "Destroyed devices." << std::endl;
336    }
337
338    // ############################################################
339    // #####                     Reloading                    #####
340    // ##########                                        ##########
341    // ############################################################
342
343    void InputManager::reload()
344    {
345        if (internalState_ & Calibrating)
346            CCOUT(2) << "Warning: Cannot reload input system. Joy sticks are currently being calibrated." << std::endl;
347        else
348            reloadInternal();
349    }
350
351    //! Internal reload method. Destroys the OIS devices and loads them again.
352    void InputManager::reloadInternal()
353    {
354        CCOUT(4) << "Reloading ..." << std::endl;
355
356        this->destroyDevices();
357        this->loadDevices();
358
359        internalState_ &= ~Bad;
360        CCOUT(4) << "Reloading complete." << std::endl;
361    }
362
363    // ############################################################
364    // #####                  Runtime Methods                 #####
365    // ##########                                        ##########
366    // ############################################################
367
368    void InputManager::preUpdate(const Clock& time)
369    {
370        if (internalState_ & Bad)
371            ThrowException(General, "InputManager was not correctly reloaded.");
372
373        // check whether a state has changed its EMPTY situation
374        bool bUpdateRequired = false;
375        for (std::map<int, InputState*>::iterator it = activeStates_.begin(); it != activeStates_.end(); ++it)
376        {
377            if (it->second->hasExpired())
378            {
379                it->second->resetExpiration();
380                bUpdateRequired = true;
381            }
382        }
383        if (bUpdateRequired)
384            updateActiveStates();
385
386        // Capture all the input and collect the function calls
387        // No event gets triggered here yet!
388        BOOST_FOREACH(InputDevice* device, devices_)
389            if (device != NULL)
390                device->update(time);
391
392        // Collect functions calls for the update
393        for (unsigned int i = 0; i < activeStatesTicked_.size(); ++i)
394            activeStatesTicked_[i]->update(time.getDeltaTime());
395
396        // Execute all cached function calls in order
397        // Why so complicated? The problem is that an InputHandler could trigger
398        // a reload that would destroy the OIS devices or it could even leave and
399        // then destroy its own InputState. That would of course lead to access
400        // violations.
401        // If we delay the calls, then OIS and and the InputStates are not anymore
402        // in the call stack and can therefore be edited.
403        for (size_t i = 0; i < this->callBuffer_.size(); ++i)
404            this->callBuffer_[i]();
405
406        this->callBuffer_.clear();
407    }
408
409    /**
410    @brief
411        Updates the currently active states (according to activeStates_) for each device.
412        Also, a list of all active states (no duplicates!) is compiled for the general preUpdate().
413    */
414    void InputManager::updateActiveStates()
415    {
416        // temporary resize
417        for (unsigned int i = 0; i < devices_.size(); ++i)
418        {
419            if (devices_[i] == NULL)
420                continue;
421            std::vector<InputState*>& states = devices_[i]->getStateListRef();
422            bool occupied = false;
423            states.clear();
424            for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin(); rit != activeStates_.rend(); ++rit)
425            {
426                if (rit->second->isInputDeviceEnabled(i) && (!occupied || rit->second->bAlwaysGetsInput_))
427                {
428                    states.push_back(rit->second);
429                    if (!rit->second->bTransparent_)
430                        occupied = true;
431                }
432            }
433        }
434
435        // update tickables (every state will only appear once)
436        // Using a std::set to avoid duplicates
437        std::set<InputState*> tempSet;
438        for (unsigned int i = 0; i < devices_.size(); ++i)
439            if (devices_[i] != NULL)
440                for (unsigned int iState = 0; iState < devices_[i]->getStateListRef().size(); ++iState)
441                    tempSet.insert(devices_[i]->getStateListRef()[iState]);
442
443        // copy the content of the std::set back to the actual vector
444        activeStatesTicked_.clear();
445        for (std::set<InputState*>::const_iterator it = tempSet.begin();it != tempSet.end(); ++it)
446            activeStatesTicked_.push_back(*it);
447
448        // Check whether we have to change the mouse mode
449        TriBool::Value requestedMode = TriBool::Dontcare;
450        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
451        if (mouseStates.empty())
452            requestedMode = TriBool::False;
453        else
454            requestedMode = mouseStates.front()->getMouseExclusive();
455        if (requestedMode != TriBool::Dontcare && exclusiveMouse_ != requestedMode)
456        {
457            exclusiveMouse_ = requestedMode;
458            if (!GraphicsManager::getInstance().isFullScreen())
459                this->reloadInternal();
460        }
461    }
462
463    void InputManager::clearBuffers()
464    {
465        BOOST_FOREACH(InputDevice* device, devices_)
466            if (device != NULL)
467                device->clearBuffers();
468    }
469
470    void InputManager::calibrate()
471    {
472        COUT(0) << "Move all joy stick axes fully in all directions." << std::endl
473                << "When done, put the axex in the middle position and press enter." << std::endl;
474
475        BOOST_FOREACH(InputDevice* device, devices_)
476            if (device != NULL)
477                device->startCalibration();
478
479        internalState_ |= Calibrating;
480        enterState("calibrator");
481    }
482
483    //! Tells all devices to stop the calibration and evaluate it. Buffers are being cleared as well!
484    void InputManager::stopCalibration()
485    {
486        BOOST_FOREACH(InputDevice* device, devices_)
487            if (device != NULL)
488                device->stopCalibration();
489
490        // restore old input state
491        leaveState("calibrator");
492        internalState_ &= ~Calibrating;
493        // Clear buffers to prevent button hold events
494        this->clearBuffers();
495
496        COUT(0) << "Calibration has been stored." << std::endl;
497    }
498
499    //! Gets called by WindowEventListener upon focus change --> clear buffers
500    void InputManager::windowFocusChanged()
501    {
502        this->clearBuffers();
503    }
504
505    std::pair<int, int> InputManager::getMousePosition() const
506    {
507        Mouse* mouse = static_cast<Mouse*>(devices_[InputDeviceEnumerator::Mouse]);
508        if (mouse != NULL)
509        {
510            const OIS::MouseState state = mouse->getOISDevice()->getMouseState();
511            return std::make_pair(state.X.abs, state.Y.abs);
512        }
513        else
514            return std::make_pair(0, 0);
515    }
516
517    // ############################################################
518    // #####                    Input States                  #####
519    // ##########                                        ##########
520    // ############################################################
521
522    InputState* InputManager::createInputState(const std::string& name, bool bAlwaysGetsInput, bool bTransparent, InputStatePriority priority)
523    {
524        if (name.empty())
525            return 0;
526        if (statesByName_.find(name) == statesByName_.end())
527        {
528            if (priority >= InputStatePriority::HighPriority || priority == InputStatePriority::Empty)
529            {
530                // Make sure we don't add two high priority states with the same priority
531                for (std::map<std::string, InputState*>::const_iterator it = this->statesByName_.begin();
532                    it != this->statesByName_.end(); ++it)
533                {
534                    if (it->second->getPriority() == priority)
535                    {
536                        COUT(2) << "Warning: Could not add an InputState with the same priority '"
537                            << static_cast<int>(priority) << "' != 0." << std::endl;
538                        return 0;
539                    }
540                }
541            }
542            InputState* state = new InputState(name, bAlwaysGetsInput, bTransparent, priority);
543            statesByName_[name] = state;
544
545            return state;
546        }
547        else
548        {
549            COUT(2) << "Warning: Could not add an InputState with the same name '" << name << "'." << std::endl;
550            return 0;
551        }
552    }
553
554    InputState* InputManager::getState(const std::string& name)
555    {
556        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
557        if (it != statesByName_.end())
558            return it->second;
559        else
560            return 0;
561    }
562
563    bool InputManager::enterState(const std::string& name)
564    {
565        // get pointer from the map with all stored handlers
566        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
567        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) == activeStates_.end())
568        {
569            // exists and not active
570            if (it->second->getPriority() == 0)
571            {
572                // Get smallest possible priority between 1 and maxStateStackSize_s
573                for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
574                    rit != activeStates_.rend(); ++rit)
575                {
576                    if (rit->first < InputStatePriority::HighPriority)
577                    {
578                        it->second->setPriority(rit->first + 1);
579                        break;
580                    }
581                }
582                // In case no normal handler was on the stack
583                if (it->second->getPriority() == 0)
584                    it->second->setPriority(1);
585            }
586            activeStates_[it->second->getPriority()] = it->second;
587            updateActiveStates();
588            it->second->entered();
589
590            return true;
591        }
592        return false;
593    }
594
595    bool InputManager::leaveState(const std::string& name)
596    {
597        if (name == "empty")
598        {
599            COUT(2) << "InputManager: Leaving the empty state is not allowed!" << std::endl;
600            return false;
601        }
602        // get pointer from the map with all stored handlers
603        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
604        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) != activeStates_.end())
605        {
606            // exists and active
607
608            it->second->left();
609
610            activeStates_.erase(it->second->getPriority());
611            if (it->second->getPriority() < InputStatePriority::HighPriority)
612                it->second->setPriority(0);
613            updateActiveStates();
614
615            return true;
616        }
617        return false;
618    }
619
620    bool InputManager::destroyState(const std::string& name)
621    {
622        if (name == "empty")
623        {
624            COUT(2) << "InputManager: Removing the empty state is not allowed!" << std::endl;
625            return false;
626        }
627        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
628        if (it != statesByName_.end())
629        {
630            this->leaveState(name);
631            destroyStateInternal(it->second);
632
633            return true;
634        }
635        return false;
636    }
637
638    //! Destroys an InputState internally.
639    void InputManager::destroyStateInternal(InputState* state)
640    {
641        assert(state && this->activeStates_.find(state->getPriority()) == this->activeStates_.end());
642        statesByName_.erase(state->getName());
643        state->destroy();
644    }
645}
Note: See TracBrowser for help on using the repository browser.