Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file was 11071, checked in by landauf, 9 years ago

merged branch cpp11_v3 back to trunk

  • Property svn:eol-style set to native
File size: 24.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 <loki/ScopeGuard.h>
42
43#include "util/Clock.h"
44#include "util/Convert.h"
45#include "util/Exception.h"
46#include "core/CoreIncludes.h"
47#include "core/GraphicsManager.h"
48#include "core/config/ConfigValueIncludes.h"
49#include "core/commandline/CommandLineIncludes.h"
50#include "core/command/ConsoleCommandIncludes.h"
51#include "core/command/Functor.h"
52
53#include "InputBuffer.h"
54#include "JoyStick.h"
55#include "JoyStickQuantityListener.h"
56#include "Mouse.h"
57#include "Keyboard.h"
58
59namespace orxonox
60{
61    SetCommandLineSwitch(keyboard_no_grab).information("Whether not to exclusively grab the keyboard");
62
63    static const std::string __CC_InputManager_name = "InputManager";
64    static const std::string __CC_calibrate_name = "calibrate";
65    static const std::string __CC_reload_name = "reload";
66
67    SetConsoleCommand(__CC_InputManager_name, __CC_calibrate_name, &InputManager::calibrate).addShortcut();
68    SetConsoleCommand(__CC_InputManager_name, __CC_reload_name,    &InputManager::reload   );
69
70    // Abuse of this source file for the InputHandler
71    InputHandler InputHandler::EMPTY;
72
73    InputManager* InputManager::singletonPtr_s = nullptr;
74
75    //! Defines the |= operator for easier use.
76    inline InputManager::State operator|=(InputManager::State& lval, InputManager::State rval)
77    {
78        return (lval = (InputManager::State)(lval | rval));
79    }
80
81    //! Defines the &= operator for easier use.
82    inline InputManager::State operator&=(InputManager::State& lval, int rval)
83    {
84        return (lval = (InputManager::State)(lval & rval));
85    }
86
87    RegisterAbstractClass(InputManager).inheritsFrom<WindowEventListener>();
88
89    // ############################################################
90    // #####                  Initialisation                  #####
91    // ##########                                        ##########
92    // ############################################################
93    InputManager::InputManager()
94        : internalState_(Bad)
95        , oisInputManager_(nullptr)
96        , devices_(2)
97        , exclusiveMouse_(false)
98        , emptyState_(nullptr)
99        , calibratorCallbackHandler_(nullptr)
100    {
101        RegisterObject(InputManager);
102
103        orxout(internal_status, context::input) << "InputManager: Constructing..." << endl;
104
105        // Allocate space for the function call buffer
106        this->callBuffer_.reserve(16);
107
108        this->setConfigValues();
109
110        if (GraphicsManager::getInstance().isFullScreen())
111            exclusiveMouse_ = true;
112        this->loadDevices();
113
114        // Lowest priority empty InputState
115        emptyState_ = createInputState("empty", false, false, InputStatePriority::Empty);
116        emptyState_->setHandler(&InputHandler::EMPTY);
117        activeStates_[emptyState_->getPriority()] = emptyState_;
118
119        // Joy stick calibration helper callback
120        InputState* calibrator = createInputState("calibrator", false, false, InputStatePriority::Calibrator);
121        calibrator->setHandler(&InputHandler::EMPTY);
122        calibratorCallbackHandler_ = new InputBuffer();
123        calibratorCallbackHandler_->registerListener(this, &InputManager::stopCalibration, '\r', true);
124        calibrator->setKeyHandler(calibratorCallbackHandler_);
125
126        this->updateActiveStates();
127
128        ModifyConsoleCommand(__CC_InputManager_name, __CC_calibrate_name).setObject(this);
129        ModifyConsoleCommand(__CC_InputManager_name, __CC_reload_name).setObject(this);
130
131        orxout(internal_status, context::input) << "InputManager: Construction complete." << endl;
132        internalState_ = Nothing;
133    }
134
135    void InputManager::setConfigValues()
136    {
137    }
138
139    /**
140    @brief
141        Creates the OIS::InputMananger, the keyboard, the mouse and
142        the joys ticks. If either of the first two fail, this method throws an exception.
143    */
144    void InputManager::loadDevices()
145    {
146        orxout(verbose, context::input) << "InputManager: Loading input devices..." << endl;
147
148        // When loading the devices they should not already be loaded
149        assert(internalState_ & Bad);
150        assert(devices_[InputDeviceEnumerator::Mouse] == nullptr);
151        assert(devices_[InputDeviceEnumerator::Keyboard] == nullptr);
152        assert(devices_.size() == InputDeviceEnumerator::FirstJoyStick);
153
154        typedef std::pair<std::string, std::string> StringPair;
155
156        // Fill parameter list
157        OIS::ParamList paramList;
158        size_t windowHnd = GraphicsManager::getInstance().getRenderWindowHandle();
159        paramList.insert(StringPair("WINDOW", multi_cast<std::string>(windowHnd)));
160#if defined(ORXONOX_PLATFORM_WINDOWS)
161        paramList.insert(StringPair("w32_keyboard", "DISCL_NONEXCLUSIVE"));
162        paramList.insert(StringPair("w32_keyboard", "DISCL_FOREGROUND"));
163        paramList.insert(StringPair("w32_mouse", "DISCL_FOREGROUND"));
164        if (exclusiveMouse_ || GraphicsManager::getInstance().isFullScreen())
165        {
166            // Disable Windows key plus special keys (like play, stop, next, etc.)
167            paramList.insert(StringPair("w32_keyboard", "DISCL_NOWINKEY"));
168            paramList.insert(StringPair("w32_mouse", "DISCL_EXCLUSIVE"));
169        }
170        else
171            paramList.insert(StringPair("w32_mouse", "DISCL_NONEXCLUSIVE"));
172#elif defined(ORXONOX_PLATFORM_LINUX)
173        // Enabling this is probably a bad idea, but whenever orxonox crashes, the setting stays on
174        // Trouble might be that the Pressed event occurs a bit too often...
175        paramList.insert(StringPair("XAutoRepeatOn", "true"));
176
177        if (exclusiveMouse_ || GraphicsManager::getInstance().isFullScreen())
178        {
179            if (CommandLineParser::getValue("keyboard_no_grab").get<bool>())
180                paramList.insert(StringPair("x11_keyboard_grab", "false"));
181            else
182                paramList.insert(StringPair("x11_keyboard_grab", "true"));
183            paramList.insert(StringPair("x11_mouse_grab",  "true"));
184            paramList.insert(StringPair("x11_mouse_hide", "true"));
185        }
186        else
187        {
188            paramList.insert(StringPair("x11_keyboard_grab", "false"));
189            paramList.insert(StringPair("x11_mouse_grab",  "false"));
190            paramList.insert(StringPair("x11_mouse_hide", "false"));
191        }
192#endif
193
194        try
195        {
196            oisInputManager_ = OIS::InputManager::createInputSystem(paramList);
197            // Exception-safety
198            Loki::ScopeGuard guard = Loki::MakeGuard(OIS::InputManager::destroyInputSystem, oisInputManager_);
199            orxout(verbose, context::input) << "Created OIS input manager." << endl;
200
201            if (oisInputManager_->getNumberOfDevices(OIS::OISKeyboard) > 0)
202                devices_[InputDeviceEnumerator::Keyboard] = new Keyboard(InputDeviceEnumerator::Keyboard, oisInputManager_);
203            else
204                ThrowException(InitialisationFailed, "InputManager: No keyboard found, cannot proceed!");
205
206            // Successful initialisation
207            guard.Dismiss();
208        }
209        catch (const std::exception& ex)
210        {
211            oisInputManager_ = nullptr;
212            internalState_ |= Bad;
213            ThrowException(InitialisationFailed, "Could not initialise the input system: " << ex.what());
214        }
215
216        this->loadMouse();
217        this->loadJoySticks();
218
219        // Reorder states in case some joy sticks were added/removed
220        this->updateActiveStates();
221
222        orxout(verbose, context::input) << "Input devices loaded." << endl;
223    }
224
225    //! Creates a new orxonox::Mouse
226    void InputManager::loadMouse()
227    {
228        if (oisInputManager_->getNumberOfDevices(OIS::OISMouse) > 0)
229        {
230            try
231            {
232                devices_[InputDeviceEnumerator::Mouse] = new Mouse(InputDeviceEnumerator::Mouse, oisInputManager_);
233            }
234            catch (const std::exception& ex)
235            {
236                orxout(user_warning, context::input) << "Failed to create Mouse:" << ex.what() << '\n'
237                                                     << "Proceeding without mouse support." << endl;
238            }
239        }
240        else
241            orxout(user_warning, context::input) << "No mouse found! Proceeding without mouse support." << endl;
242    }
243
244    //! Creates as many joy sticks as are available.
245    void InputManager::loadJoySticks()
246    {
247        for (int i = 0; i < oisInputManager_->getNumberOfDevices(OIS::OISJoyStick); i++)
248        {
249            try
250            {
251                devices_.push_back(new JoyStick(InputDeviceEnumerator::FirstJoyStick + i, oisInputManager_));
252            }
253            catch (const std::exception& ex)
254            {
255                orxout(user_warning, context::input) << "Failed to create joy stick: " << ex.what() << endl;
256            }
257        }
258
259        // inform all JoyStick Device Number Listeners
260        std::vector<JoyStick*> joyStickList;
261        for (unsigned int i = InputDeviceEnumerator::FirstJoyStick; i < devices_.size(); ++i)
262            joyStickList.push_back(static_cast<JoyStick*>(devices_[i]));
263        JoyStickQuantityListener::changeJoyStickQuantity(joyStickList);
264    }
265
266    // ############################################################
267    // #####                    Destruction                   #####
268    // ##########                                        ##########
269    // ############################################################
270
271    InputManager::~InputManager()
272    {
273        orxout(internal_status, context::input) << "InputManager: Destroying..." << endl;
274
275        // Leave all active InputStates (except "empty")
276        while (this->activeStates_.size() > 1)
277            this->leaveState(this->activeStates_.rbegin()->second->getName());
278        this->activeStates_.clear();
279
280        // Destroy calibrator helper handler and state
281        this->destroyState("calibrator");
282        // Destroy KeyDetector and state
283        delete calibratorCallbackHandler_;
284        // Destroy the empty InputState
285        this->destroyStateInternal(this->emptyState_);
286
287        // Destroy all user InputStates
288        while (statesByName_.size() > 0)
289            this->destroyStateInternal(statesByName_.rbegin()->second);
290
291        if (!(internalState_ & Bad))
292            this->destroyDevices();
293
294        // Reset console commands
295        ModifyConsoleCommand(__CC_InputManager_name, __CC_calibrate_name).setObject(nullptr);
296        ModifyConsoleCommand(__CC_InputManager_name, __CC_reload_name).setObject(nullptr);
297
298        orxout(internal_status, context::input) << "InputManager: Destruction complete." << endl;
299    }
300
301    /**
302    @brief
303        Destoys all input devices (joy sticks, mouse, keyboard and OIS::InputManager)
304    @throw
305        Method does not throw
306    */
307    void InputManager::destroyDevices()
308    {
309        orxout(verbose, context::input) << "InputManager: Destroying devices..." << endl;
310
311        for (InputDevice*& device : devices_)
312        {
313            if (device == nullptr)
314                continue;
315            const std::string& className = device->getClassName();
316            delete device;
317            device = nullptr;
318            orxout(verbose, context::input) << className << " destroyed." << endl;
319        }
320        devices_.resize(InputDeviceEnumerator::FirstJoyStick);
321
322        assert(oisInputManager_ != nullptr);
323        try
324        {
325            OIS::InputManager::destroyInputSystem(oisInputManager_);
326        }
327        catch (const OIS::Exception& ex)
328        {
329            orxout(internal_error, context::input) << "OIS::InputManager destruction failed" << ex.eText << '\n'
330                                                   << "Potential resource leak!" << endl;
331        }
332        oisInputManager_ = nullptr;
333
334        internalState_ |= Bad;
335        orxout(verbose, context::input) << "Destroyed devices." << endl;
336    }
337
338    // ############################################################
339    // #####                     Reloading                    #####
340    // ##########                                        ##########
341    // ############################################################
342
343    void InputManager::reload()
344    {
345        if (internalState_ & Calibrating)
346            orxout(internal_warning, context::input) << "Cannot reload input system. Joy sticks are currently being calibrated." << 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        orxout(verbose, context::input) << "InputManager: Reloading ..." << endl;
355
356        this->destroyDevices();
357        this->loadDevices();
358
359        internalState_ &= ~Bad;
360        orxout(verbose, context::input) << "InputManager: Reloading complete." << 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 (const auto& mapEntry : activeStates_)
376        {
377            if (mapEntry.second->hasExpired())
378            {
379                mapEntry.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        for  (InputDevice* device : devices_)
389            if (device != nullptr)
390                device->update(time);
391
392        // Collect function calls for the update
393        for (InputState* state : activeStatesTicked_)
394            state->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 (auto& function : this->callBuffer_)
404            function();
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] == nullptr)
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 (InputDevice* device : devices_)
440            if (device != nullptr)
441                for (unsigned int iState = 0; iState < device->getStateListRef().size(); ++iState)
442                    tempSet.insert(device->getStateListRef()[iState]);
443
444        // Copy the content of the std::set back to the actual vector
445        activeStatesTicked_.clear();
446        for (InputState* state : tempSet)
447            activeStatesTicked_.push_back(state);
448
449        // Check whether we have to change the mouse mode
450        tribool requestedMode = dontcare;
451        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
452        if (mouseStates.empty())
453            requestedMode = false;
454        else
455            requestedMode = mouseStates.front()->getMouseExclusive();
456        if (requestedMode != dontcare && exclusiveMouse_ != requestedMode)
457        {
458            assert(requestedMode != dontcare);
459            exclusiveMouse_ = (requestedMode == true);
460            if (!GraphicsManager::getInstance().isFullScreen())
461                this->reloadInternal();
462        }
463    }
464
465    void InputManager::clearBuffers()
466    {
467        for (InputDevice* device : devices_)
468            if (device != nullptr)
469                device->clearBuffers();
470    }
471
472    void InputManager::calibrate()
473    {
474        orxout(message) << "Move all joy stick axes fully in all directions." << '\n'
475                        << "When done, put the axex in the middle position and press enter." << endl;
476
477        for (InputDevice* device : devices_)
478            if (device != nullptr)
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        for (InputDevice* device : devices_)
489            if (device != nullptr)
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        orxout(message) << "Calibration has been stored." << endl;
499    }
500
501    //! Gets called by WindowEventListener upon focus change --> clear buffers
502    void InputManager::windowFocusChanged(bool bFocus)
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 != nullptr)
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 nullptr;
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 (const auto& mapEntry : this->statesByName_)
534                {
535                    if (mapEntry.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 nullptr;
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 nullptr;
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 nullptr;
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        delete state;
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.