Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9550 was 9550, checked in by landauf, 11 years ago

merged testing branch back to trunk. unbelievable it took me 13 months to finish this chore…

  • Property svn:eol-style set to native
File size: 24.8 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_(false)
97        , emptyState_(0)
98        , calibratorCallbackHandler_(0)
99    {
100        RegisterRootObject(InputManager);
101
102        orxout(internal_status, context::input) << "InputManager: Constructing..." << 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_ = 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        orxout(internal_status, context::input) << "InputManager: Construction complete." << 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    */
143    void InputManager::loadDevices()
144    {
145        orxout(verbose, context::input) << "InputManager: Loading input devices..." << endl;
146
147        // When loading the devices they should not already be loaded
148        assert(internalState_ & Bad);
149        assert(devices_[InputDeviceEnumerator::Mouse] == 0);
150        assert(devices_[InputDeviceEnumerator::Keyboard] == 0);
151        assert(devices_.size() == InputDeviceEnumerator::FirstJoyStick);
152
153        typedef std::pair<std::string, std::string> StringPair;
154
155        // Fill parameter list
156        OIS::ParamList paramList;
157        size_t windowHnd = GraphicsManager::getInstance().getRenderWindowHandle();
158        paramList.insert(StringPair("WINDOW", multi_cast<std::string>(windowHnd)));
159#if defined(ORXONOX_PLATFORM_WINDOWS)
160        paramList.insert(StringPair("w32_keyboard", "DISCL_NONEXCLUSIVE"));
161        paramList.insert(StringPair("w32_keyboard", "DISCL_FOREGROUND"));
162        paramList.insert(StringPair("w32_mouse", "DISCL_FOREGROUND"));
163        if (exclusiveMouse_ || GraphicsManager::getInstance().isFullScreen())
164        {
165            // Disable Windows key plus special keys (like play, stop, next, etc.)
166            paramList.insert(StringPair("w32_keyboard", "DISCL_NOWINKEY"));
167            paramList.insert(StringPair("w32_mouse", "DISCL_EXCLUSIVE"));
168        }
169        else
170            paramList.insert(StringPair("w32_mouse", "DISCL_NONEXCLUSIVE"));
171#elif defined(ORXONOX_PLATFORM_LINUX)
172        // Enabling this is probably a bad idea, but whenever orxonox crashes, the setting stays on
173        // Trouble might be that the Pressed event occurs a bit too often...
174        paramList.insert(StringPair("XAutoRepeatOn", "true"));
175
176        if (exclusiveMouse_ || GraphicsManager::getInstance().isFullScreen())
177        {
178            if (CommandLineParser::getValue("keyboard_no_grab").get<bool>())
179                paramList.insert(StringPair("x11_keyboard_grab", "false"));
180            else
181                paramList.insert(StringPair("x11_keyboard_grab", "true"));
182            paramList.insert(StringPair("x11_mouse_grab",  "true"));
183            paramList.insert(StringPair("x11_mouse_hide", "true"));
184        }
185        else
186        {
187            paramList.insert(StringPair("x11_keyboard_grab", "false"));
188            paramList.insert(StringPair("x11_mouse_grab",  "false"));
189            paramList.insert(StringPair("x11_mouse_hide", "false"));
190        }
191#endif
192
193        try
194        {
195            oisInputManager_ = OIS::InputManager::createInputSystem(paramList);
196            // Exception-safety
197            Loki::ScopeGuard guard = Loki::MakeGuard(OIS::InputManager::destroyInputSystem, oisInputManager_);
198            orxout(verbose, context::input) << "Created OIS input manager." << endl;
199
200            if (oisInputManager_->getNumberOfDevices(OIS::OISKeyboard) > 0)
201                devices_[InputDeviceEnumerator::Keyboard] = new Keyboard(InputDeviceEnumerator::Keyboard, oisInputManager_);
202            else
203                ThrowException(InitialisationFailed, "InputManager: No keyboard found, cannot proceed!");
204
205            // Successful initialisation
206            guard.Dismiss();
207        }
208        catch (const std::exception& ex)
209        {
210            oisInputManager_ = NULL;
211            internalState_ |= Bad;
212            ThrowException(InitialisationFailed, "Could not initialise the input system: " << ex.what());
213        }
214
215        this->loadMouse();
216        this->loadJoySticks();
217
218        // Reorder states in case some joy sticks were added/removed
219        this->updateActiveStates();
220
221        orxout(verbose, context::input) << "Input devices loaded." << endl;
222    }
223
224    //! Creates a new orxonox::Mouse
225    void InputManager::loadMouse()
226    {
227        if (oisInputManager_->getNumberOfDevices(OIS::OISMouse) > 0)
228        {
229            try
230            {
231                devices_[InputDeviceEnumerator::Mouse] = new Mouse(InputDeviceEnumerator::Mouse, oisInputManager_);
232            }
233            catch (const std::exception& ex)
234            {
235                orxout(user_warning, context::input) << "Failed to create Mouse:" << ex.what() << '\n'
236                                                     << "Proceeding without mouse support." << endl;
237            }
238        }
239        else
240            orxout(user_warning, context::input) << "No mouse found! Proceeding without mouse support." << endl;
241    }
242
243    //! Creates as many joy sticks as are available.
244    void InputManager::loadJoySticks()
245    {
246        for (int i = 0; i < oisInputManager_->getNumberOfDevices(OIS::OISJoyStick); i++)
247        {
248            try
249            {
250                devices_.push_back(new JoyStick(InputDeviceEnumerator::FirstJoyStick + i, oisInputManager_));
251            }
252            catch (const std::exception& ex)
253            {
254                orxout(user_warning, context::input) << "Failed to create joy stick: " << ex.what() << endl;
255            }
256        }
257
258        // inform all JoyStick Device Number Listeners
259        std::vector<JoyStick*> joyStickList;
260        for (unsigned int i = InputDeviceEnumerator::FirstJoyStick; i < devices_.size(); ++i)
261            joyStickList.push_back(static_cast<JoyStick*>(devices_[i]));
262        JoyStickQuantityListener::changeJoyStickQuantity(joyStickList);
263    }
264
265    // ############################################################
266    // #####                    Destruction                   #####
267    // ##########                                        ##########
268    // ############################################################
269
270    InputManager::~InputManager()
271    {
272        orxout(internal_status, context::input) << "InputManager: Destroying..." << endl;
273
274        // Leave all active InputStates (except "empty")
275        while (this->activeStates_.size() > 1)
276            this->leaveState(this->activeStates_.rbegin()->second->getName());
277        this->activeStates_.clear();
278
279        // Destroy calibrator helper handler and state
280        this->destroyState("calibrator");
281        // Destroy KeyDetector and state
282        calibratorCallbackHandler_->destroy();
283        // Destroy the empty InputState
284        this->destroyStateInternal(this->emptyState_);
285
286        // Destroy all user InputStates
287        while (statesByName_.size() > 0)
288            this->destroyStateInternal(statesByName_.rbegin()->second);
289
290        if (!(internalState_ & Bad))
291            this->destroyDevices();
292
293        // Reset console commands
294        ModifyConsoleCommand(__CC_InputManager_name, __CC_calibrate_name).setObject(0);
295        ModifyConsoleCommand(__CC_InputManager_name, __CC_reload_name).setObject(0);
296
297        orxout(internal_status, context::input) << "InputManager: Destruction complete." << endl;
298    }
299
300    /**
301    @brief
302        Destoys all input devices (joy sticks, mouse, keyboard and OIS::InputManager)
303    @throw
304        Method does not throw
305    */
306    void InputManager::destroyDevices()
307    {
308        orxout(verbose, context::input) << "InputManager: Destroying devices..." << endl;
309
310        BOOST_FOREACH(InputDevice*& device, devices_)
311        {
312            if (device == NULL)
313                continue;
314            const std::string& className = device->getClassName();
315            delete device;
316            device = 0;
317            orxout(verbose, context::input) << className << " destroyed." << endl;
318        }
319        devices_.resize(InputDeviceEnumerator::FirstJoyStick);
320
321        assert(oisInputManager_ != NULL);
322        try
323        {
324            OIS::InputManager::destroyInputSystem(oisInputManager_);
325        }
326        catch (const OIS::Exception& ex)
327        {
328            orxout(internal_error, context::input) << "OIS::InputManager destruction failed" << ex.eText << '\n'
329                                                   << "Potential resource leak!" << endl;
330        }
331        oisInputManager_ = NULL;
332
333        internalState_ |= Bad;
334        orxout(verbose, context::input) << "Destroyed devices." << endl;
335    }
336
337    // ############################################################
338    // #####                     Reloading                    #####
339    // ##########                                        ##########
340    // ############################################################
341
342    void InputManager::reload()
343    {
344        if (internalState_ & Calibrating)
345            orxout(internal_warning, context::input) << "Cannot reload input system. Joy sticks are currently being calibrated." << endl;
346        else
347            reloadInternal();
348    }
349
350    //! Internal reload method. Destroys the OIS devices and loads them again.
351    void InputManager::reloadInternal()
352    {
353        orxout(verbose, context::input) << "InputManager: Reloading ..." << endl;
354
355        this->destroyDevices();
356        this->loadDevices();
357
358        internalState_ &= ~Bad;
359        orxout(verbose, context::input) << "InputManager: Reloading complete." << endl;
360    }
361
362    // ############################################################
363    // #####                  Runtime Methods                 #####
364    // ##########                                        ##########
365    // ############################################################
366
367    void InputManager::preUpdate(const Clock& time)
368    {
369        if (internalState_ & Bad)
370            ThrowException(General, "InputManager was not correctly reloaded.");
371
372        // check whether a state has changed its EMPTY situation
373        bool bUpdateRequired = false;
374        for (std::map<int, InputState*>::iterator it = activeStates_.begin(); it != activeStates_.end(); ++it)
375        {
376            if (it->second->hasExpired())
377            {
378                it->second->resetExpiration();
379                bUpdateRequired = true;
380            }
381        }
382        if (bUpdateRequired)
383            updateActiveStates();
384
385        // Capture all the input and collect the function calls
386        // No event gets triggered here yet!
387        BOOST_FOREACH(InputDevice* device, devices_)
388            if (device != NULL)
389                device->update(time);
390
391        // Collect function calls for the update
392        for (unsigned int i = 0; i < activeStatesTicked_.size(); ++i)
393            activeStatesTicked_[i]->update(time.getDeltaTime());
394
395        // Execute all cached function calls in order
396        // Why so complicated? The problem is that an InputHandler could trigger
397        // a reload that would destroy the OIS devices or it could even leave and
398        // then destroy its own InputState. That would of course lead to access
399        // violations.
400        // If we delay the calls, then OIS and and the InputStates are not anymore
401        // in the call stack and can therefore be edited.
402        for (size_t i = 0; i < this->callBuffer_.size(); ++i)
403            this->callBuffer_[i]();
404
405        this->callBuffer_.clear();
406    }
407
408    /**
409    @brief
410        Updates the currently active states (according to activeStates_) for each device.
411        Also, a list of all active states (no duplicates!) is compiled for the general preUpdate().
412    */
413    void InputManager::updateActiveStates()
414    {
415        // Calculate the stack of input states
416        // and assign it to the corresponding device
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        // See that we only update each InputState once for each device
436        // Using an 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 requestedMode = dontcare;
450        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
451        if (mouseStates.empty())
452            requestedMode = false;
453        else
454            requestedMode = mouseStates.front()->getMouseExclusive();
455        if (requestedMode != dontcare && exclusiveMouse_ != requestedMode)
456        {
457            assert(requestedMode != dontcare);
458            exclusiveMouse_ = (requestedMode == true);
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        orxout(message) << "Move all joy stick axes fully in all directions." << '\n'
474                        << "When done, put the axex in the middle position and press enter." << 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        orxout(message) << "Calibration has been stored." << endl;
498    }
499
500    //! Gets called by WindowEventListener upon focus change --> clear buffers
501    void InputManager::windowFocusChanged(bool bFocus)
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                        orxout(internal_warning, context::input) << "Could not add an InputState with the same priority '"
538                            << static_cast<int>(priority) << "' != 0." << 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            orxout(internal_warning, context::input) << "Could not add an InputState with the same name '" << name << "'." << 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            orxout(internal_warning, context::input) << "InputManager: Leaving the empty state is not allowed!" << 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            orxout(internal_warning, context::input) << "InputManager: Removing the empty state is not allowed!" << 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
647    bool InputManager::setMouseExclusive(const std::string& name, tribool value)
648    {
649        if (name == "empty")
650        {
651            orxout(internal_warning, context::input) << "InputManager: Changing the empty state is not allowed!" << endl;
652            return false;
653        }
654        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
655        if (it != statesByName_.end())
656        {
657            it->second->setMouseExclusive(value);
658            return true;
659        }
660        return false;
661    }
662}
Note: See TracBrowser for help on using the repository browser.