Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7073 was 6746, checked in by rgrieder, 15 years ago

Merged gamestates2 branch back to trunk.
This brings in some heavy changes in the GUI framework.
It should also fix problems with triggered asserts in the InputManager.

Note: PickupInventory does not seem to work —> Segfault when showing because before, the owner in GUIOverlay::setGUIName is already NULL.
I haven't tested it before, so I can't tell whether it's my changes.

  • Property svn:eol-style set to native
File size: 23.7 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 function 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        // Calculate the stack of input states
417        // and assign it to the corresponding device
418        for (unsigned int i = 0; i < devices_.size(); ++i)
419        {
420            if (devices_[i] == NULL)
421                continue;
422            std::vector<InputState*>& states = devices_[i]->getStateListRef();
423            bool occupied = false;
424            states.clear();
425            for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin(); rit != activeStates_.rend(); ++rit)
426            {
427                if (rit->second->isInputDeviceEnabled(i) && (!occupied || rit->second->bAlwaysGetsInput_))
428                {
429                    states.push_back(rit->second);
430                    if (!rit->second->bTransparent_)
431                        occupied = true;
432                }
433            }
434        }
435
436        // See that we only update each InputState once for each device
437        // Using an std::set to avoid duplicates
438        std::set<InputState*> tempSet;
439        for (unsigned int i = 0; i < devices_.size(); ++i)
440            if (devices_[i] != NULL)
441                for (unsigned int iState = 0; iState < devices_[i]->getStateListRef().size(); ++iState)
442                    tempSet.insert(devices_[i]->getStateListRef()[iState]);
443
444        // Copy the content of the std::set back to the actual vector
445        activeStatesTicked_.clear();
446        for (std::set<InputState*>::const_iterator it = tempSet.begin();it != tempSet.end(); ++it)
447            activeStatesTicked_.push_back(*it);
448
449        // Check whether we have to change the mouse mode
450        TriBool::Value requestedMode = TriBool::Dontcare;
451        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
452        if (mouseStates.empty())
453            requestedMode = TriBool::False;
454        else
455            requestedMode = mouseStates.front()->getMouseExclusive();
456        if (requestedMode != TriBool::Dontcare && exclusiveMouse_ != requestedMode)
457        {
458            exclusiveMouse_ = requestedMode;
459            if (!GraphicsManager::getInstance().isFullScreen())
460                this->reloadInternal();
461        }
462    }
463
464    void InputManager::clearBuffers()
465    {
466        BOOST_FOREACH(InputDevice* device, devices_)
467            if (device != NULL)
468                device->clearBuffers();
469    }
470
471    void InputManager::calibrate()
472    {
473        COUT(0) << "Move all joy stick axes fully in all directions." << std::endl
474                << "When done, put the axex in the middle position and press enter." << std::endl;
475
476        BOOST_FOREACH(InputDevice* device, devices_)
477            if (device != NULL)
478                device->startCalibration();
479
480        internalState_ |= Calibrating;
481        enterState("calibrator");
482    }
483
484    //! Tells all devices to stop the calibration and evaluate it. Buffers are being cleared as well!
485    void InputManager::stopCalibration()
486    {
487        BOOST_FOREACH(InputDevice* device, devices_)
488            if (device != NULL)
489                device->stopCalibration();
490
491        // restore old input state
492        leaveState("calibrator");
493        internalState_ &= ~Calibrating;
494        // Clear buffers to prevent button hold events
495        this->clearBuffers();
496
497        COUT(0) << "Calibration has been stored." << std::endl;
498    }
499
500    //! Gets called by WindowEventListener upon focus change --> clear buffers
501    void InputManager::windowFocusChanged()
502    {
503        this->clearBuffers();
504    }
505
506    std::pair<int, int> InputManager::getMousePosition() const
507    {
508        Mouse* mouse = static_cast<Mouse*>(devices_[InputDeviceEnumerator::Mouse]);
509        if (mouse != NULL)
510        {
511            const OIS::MouseState state = mouse->getOISDevice()->getMouseState();
512            return std::make_pair(state.X.abs, state.Y.abs);
513        }
514        else
515            return std::make_pair(0, 0);
516    }
517
518    // ############################################################
519    // #####                    Input States                  #####
520    // ##########                                        ##########
521    // ############################################################
522
523    InputState* InputManager::createInputState(const std::string& name, bool bAlwaysGetsInput, bool bTransparent, InputStatePriority priority)
524    {
525        if (name.empty())
526            return 0;
527        if (statesByName_.find(name) == statesByName_.end())
528        {
529            if (priority >= InputStatePriority::HighPriority || priority == InputStatePriority::Empty)
530            {
531                // Make sure we don't add two high priority states with the same priority
532                for (std::map<std::string, InputState*>::const_iterator it = this->statesByName_.begin();
533                    it != this->statesByName_.end(); ++it)
534                {
535                    if (it->second->getPriority() == priority)
536                    {
537                        COUT(2) << "Warning: Could not add an InputState with the same priority '"
538                            << static_cast<int>(priority) << "' != 0." << std::endl;
539                        return 0;
540                    }
541                }
542            }
543            InputState* state = new InputState(name, bAlwaysGetsInput, bTransparent, priority);
544            statesByName_[name] = state;
545
546            return state;
547        }
548        else
549        {
550            COUT(2) << "Warning: Could not add an InputState with the same name '" << name << "'." << std::endl;
551            return 0;
552        }
553    }
554
555    InputState* InputManager::getState(const std::string& name)
556    {
557        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
558        if (it != statesByName_.end())
559            return it->second;
560        else
561            return 0;
562    }
563
564    bool InputManager::enterState(const std::string& name)
565    {
566        // get pointer from the map with all stored handlers
567        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
568        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) == activeStates_.end())
569        {
570            // exists and not active
571            if (it->second->getPriority() == 0)
572            {
573                // Get smallest possible priority between 1 and maxStateStackSize_s
574                for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
575                    rit != activeStates_.rend(); ++rit)
576                {
577                    if (rit->first < InputStatePriority::HighPriority)
578                    {
579                        it->second->setPriority(rit->first + 1);
580                        break;
581                    }
582                }
583                // In case no normal handler was on the stack
584                if (it->second->getPriority() == 0)
585                    it->second->setPriority(1);
586            }
587            activeStates_[it->second->getPriority()] = it->second;
588            updateActiveStates();
589            it->second->entered();
590
591            return true;
592        }
593        return false;
594    }
595
596    bool InputManager::leaveState(const std::string& name)
597    {
598        if (name == "empty")
599        {
600            COUT(2) << "InputManager: Leaving the empty state is not allowed!" << std::endl;
601            return false;
602        }
603        // get pointer from the map with all stored handlers
604        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
605        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) != activeStates_.end())
606        {
607            // exists and active
608
609            it->second->left();
610
611            activeStates_.erase(it->second->getPriority());
612            if (it->second->getPriority() < InputStatePriority::HighPriority)
613                it->second->setPriority(0);
614            updateActiveStates();
615
616            return true;
617        }
618        return false;
619    }
620
621    bool InputManager::destroyState(const std::string& name)
622    {
623        if (name == "empty")
624        {
625            COUT(2) << "InputManager: Removing the empty state is not allowed!" << std::endl;
626            return false;
627        }
628        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
629        if (it != statesByName_.end())
630        {
631            this->leaveState(name);
632            destroyStateInternal(it->second);
633
634            return true;
635        }
636        return false;
637    }
638
639    //! Destroys an InputState internally.
640    void InputManager::destroyStateInternal(InputState* state)
641    {
642        assert(state && this->activeStates_.find(state->getPriority()) == this->activeStates_.end());
643        statesByName_.erase(state->getName());
644        state->destroy();
645    }
646}
Note: See TracBrowser for help on using the repository browser.