Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7874 was 7874, checked in by landauf, 13 years ago

WindowEventListener now declares if the window's focus is active or not.
CEGUI stops highlighting menu items if the window's focus is lost, i.e. after pressing alt+tab

  • Property svn:eol-style set to native
File size: 23.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_(TriBool::False)
97        , emptyState_(0)
98        , calibratorCallbackHandler_(0)
99    {
100        RegisterRootObject(InputManager);
101
102        CCOUT(4) << "Constructing..." << std::endl;
103
104        // Allocate space for the function call buffer
105        this->callBuffer_.reserve(16);
106
107        this->setConfigValues();
108
109        if (GraphicsManager::getInstance().isFullScreen())
110            exclusiveMouse_ = TriBool::True;
111        this->loadDevices();
112
113        // Lowest priority empty InputState
114        emptyState_ = createInputState("empty", false, false, InputStatePriority::Empty);
115        emptyState_->setHandler(&InputHandler::EMPTY);
116        activeStates_[emptyState_->getPriority()] = emptyState_;
117
118        // Joy stick calibration helper callback
119        InputState* calibrator = createInputState("calibrator", false, false, InputStatePriority::Calibrator);
120        calibrator->setHandler(&InputHandler::EMPTY);
121        calibratorCallbackHandler_ = new InputBuffer();
122        calibratorCallbackHandler_->registerListener(this, &InputManager::stopCalibration, '\r', true);
123        calibrator->setKeyHandler(calibratorCallbackHandler_);
124
125        this->updateActiveStates();
126
127        ModifyConsoleCommand(__CC_InputManager_name, __CC_calibrate_name).setObject(this);
128        ModifyConsoleCommand(__CC_InputManager_name, __CC_reload_name).setObject(this);
129
130        CCOUT(4) << "Construction complete." << std::endl;
131        internalState_ = Nothing;
132    }
133
134    void InputManager::setConfigValues()
135    {
136    }
137
138    /**
139    @brief
140        Creates the OIS::InputMananger, the keyboard, the mouse and
141        the joys ticks. If either of the first two fail, this method throws an exception.
142    */
143    void InputManager::loadDevices()
144    {
145        CCOUT(4) << "Loading input devices..." << std::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        // Fill parameter list
154        OIS::ParamList paramList;
155        size_t windowHnd = GraphicsManager::getInstance().getRenderWindowHandle();
156        paramList.insert(std::make_pair("WINDOW", multi_cast<std::string>(windowHnd)));
157#if defined(ORXONOX_PLATFORM_WINDOWS)
158        paramList.insert(std::make_pair("w32_keyboard", "DISCL_NONEXCLUSIVE"));
159        paramList.insert(std::make_pair("w32_keyboard", "DISCL_FOREGROUND"));
160        paramList.insert(std::make_pair("w32_mouse", "DISCL_FOREGROUND"));
161        if (exclusiveMouse_ == TriBool::True || GraphicsManager::getInstance().isFullScreen())
162        {
163            // Disable Windows key plus special keys (like play, stop, next, etc.)
164            paramList.insert(std::make_pair("w32_keyboard", "DISCL_NOWINKEY"));
165            paramList.insert(std::make_pair("w32_mouse", "DISCL_EXCLUSIVE"));
166        }
167        else
168            paramList.insert(std::make_pair("w32_mouse", "DISCL_NONEXCLUSIVE"));
169#elif defined(ORXONOX_PLATFORM_LINUX)
170        // Enabling this is probably a bad idea, but whenever orxonox crashes, the setting stays on
171        // Trouble might be that the Pressed event occurs a bit too often...
172        paramList.insert(std::make_pair("XAutoRepeatOn", "true"));
173
174        if (exclusiveMouse_ == TriBool::True || GraphicsManager::getInstance().isFullScreen())
175        {
176            if (CommandLineParser::getValue("keyboard_no_grab").getBool())
177                paramList.insert(std::make_pair("x11_keyboard_grab", "false"));
178            else
179                paramList.insert(std::make_pair("x11_keyboard_grab", "true"));
180            paramList.insert(std::make_pair("x11_mouse_grab",  "true"));
181            paramList.insert(std::make_pair("x11_mouse_hide", "true"));
182        }
183        else
184        {
185            paramList.insert(std::make_pair("x11_keyboard_grab", "false"));
186            paramList.insert(std::make_pair("x11_mouse_grab",  "false"));
187            paramList.insert(std::make_pair("x11_mouse_hide", "false"));
188        }
189#endif
190
191        try
192        {
193            oisInputManager_ = OIS::InputManager::createInputSystem(paramList);
194            // Exception-safety
195            Loki::ScopeGuard guard = Loki::MakeGuard(OIS::InputManager::destroyInputSystem, oisInputManager_);
196            CCOUT(4) << "Created OIS input manager." << std::endl;
197
198            if (oisInputManager_->getNumberOfDevices(OIS::OISKeyboard) > 0)
199                devices_[InputDeviceEnumerator::Keyboard] = new Keyboard(InputDeviceEnumerator::Keyboard, oisInputManager_);
200            else
201                ThrowException(InitialisationFailed, "InputManager: No keyboard found, cannot proceed!");
202
203            // Successful initialisation
204            guard.Dismiss();
205        }
206        catch (const std::exception& ex)
207        {
208            oisInputManager_ = NULL;
209            internalState_ |= Bad;
210            ThrowException(InitialisationFailed, "Could not initialise the input system: " << ex.what());
211        }
212
213        this->loadMouse();
214        this->loadJoySticks();
215
216        // Reorder states in case some joy sticks were added/removed
217        this->updateActiveStates();
218
219        CCOUT(4) << "Input devices loaded." << std::endl;
220    }
221
222    //! Creates a new orxonox::Mouse
223    void InputManager::loadMouse()
224    {
225        if (oisInputManager_->getNumberOfDevices(OIS::OISMouse) > 0)
226        {
227            try
228            {
229                devices_[InputDeviceEnumerator::Mouse] = new Mouse(InputDeviceEnumerator::Mouse, oisInputManager_);
230            }
231            catch (const std::exception& ex)
232            {
233                CCOUT(2) << "Warning: Failed to create Mouse:" << ex.what() << std::endl
234                         << "Proceeding without mouse support." << std::endl;
235            }
236        }
237        else
238            CCOUT(2) << "Warning: No mouse found! Proceeding without mouse support." << std::endl;
239    }
240
241    //! Creates as many joy sticks as are available.
242    void InputManager::loadJoySticks()
243    {
244        for (int i = 0; i < oisInputManager_->getNumberOfDevices(OIS::OISJoyStick); i++)
245        {
246            try
247            {
248                devices_.push_back(new JoyStick(InputDeviceEnumerator::FirstJoyStick + i, oisInputManager_));
249            }
250            catch (const std::exception& ex)
251            {
252                CCOUT(2) << "Warning: Failed to create joy stick: " << ex.what() << std::endl;
253            }
254        }
255
256        // inform all JoyStick Device Number Listeners
257        std::vector<JoyStick*> joyStickList;
258        for (unsigned int i = InputDeviceEnumerator::FirstJoyStick; i < devices_.size(); ++i)
259            joyStickList.push_back(static_cast<JoyStick*>(devices_[i]));
260        JoyStickQuantityListener::changeJoyStickQuantity(joyStickList);
261    }
262
263    // ############################################################
264    // #####                    Destruction                   #####
265    // ##########                                        ##########
266    // ############################################################
267
268    InputManager::~InputManager()
269    {
270        CCOUT(3) << "Destroying..." << std::endl;
271
272        // Leave all active InputStates (except "empty")
273        while (this->activeStates_.size() > 1)
274            this->leaveState(this->activeStates_.rbegin()->second->getName());
275        this->activeStates_.clear();
276
277        // Destroy calibrator helper handler and state
278        this->destroyState("calibrator");
279        // Destroy KeyDetector and state
280        calibratorCallbackHandler_->destroy();
281        // Destroy the empty InputState
282        this->destroyStateInternal(this->emptyState_);
283
284        // Destroy all user InputStates
285        while (statesByName_.size() > 0)
286            this->destroyStateInternal(statesByName_.rbegin()->second);
287
288        if (!(internalState_ & Bad))
289            this->destroyDevices();
290
291        // Reset console commands
292        ModifyConsoleCommand(__CC_InputManager_name, __CC_calibrate_name).setObject(0);
293        ModifyConsoleCommand(__CC_InputManager_name, __CC_reload_name).setObject(0);
294
295        CCOUT(3) << "Destruction complete." << std::endl;
296    }
297
298    /**
299    @brief
300        Destoys all input devices (joy sticks, mouse, keyboard and OIS::InputManager)
301    @throw
302        Method does not throw
303    */
304    void InputManager::destroyDevices()
305    {
306        CCOUT(4) << "Destroying devices..." << std::endl;
307
308        BOOST_FOREACH(InputDevice*& device, devices_)
309        {
310            if (device == NULL)
311                continue;
312            const std::string& className = device->getClassName();
313            delete device;
314            device = 0;
315            CCOUT(4) << className << " destroyed." << std::endl;
316        }
317        devices_.resize(InputDeviceEnumerator::FirstJoyStick);
318
319        assert(oisInputManager_ != NULL);
320        try
321        {
322            OIS::InputManager::destroyInputSystem(oisInputManager_);
323        }
324        catch (const OIS::Exception& ex)
325        {
326            COUT(1) << "OIS::InputManager destruction failed" << ex.eText << std::endl
327                    << "    Potential resource leak!" << std::endl;
328        }
329        oisInputManager_ = NULL;
330
331        internalState_ |= Bad;
332        CCOUT(4) << "Destroyed devices." << std::endl;
333    }
334
335    // ############################################################
336    // #####                     Reloading                    #####
337    // ##########                                        ##########
338    // ############################################################
339
340    void InputManager::reload()
341    {
342        if (internalState_ & Calibrating)
343            CCOUT(2) << "Warning: Cannot reload input system. Joy sticks are currently being calibrated." << std::endl;
344        else
345            reloadInternal();
346    }
347
348    //! Internal reload method. Destroys the OIS devices and loads them again.
349    void InputManager::reloadInternal()
350    {
351        CCOUT(4) << "Reloading ..." << std::endl;
352
353        this->destroyDevices();
354        this->loadDevices();
355
356        internalState_ &= ~Bad;
357        CCOUT(4) << "Reloading complete." << std::endl;
358    }
359
360    // ############################################################
361    // #####                  Runtime Methods                 #####
362    // ##########                                        ##########
363    // ############################################################
364
365    void InputManager::preUpdate(const Clock& time)
366    {
367        if (internalState_ & Bad)
368            ThrowException(General, "InputManager was not correctly reloaded.");
369
370        // check whether a state has changed its EMPTY situation
371        bool bUpdateRequired = false;
372        for (std::map<int, InputState*>::iterator it = activeStates_.begin(); it != activeStates_.end(); ++it)
373        {
374            if (it->second->hasExpired())
375            {
376                it->second->resetExpiration();
377                bUpdateRequired = true;
378            }
379        }
380        if (bUpdateRequired)
381            updateActiveStates();
382
383        // Capture all the input and collect the function calls
384        // No event gets triggered here yet!
385        BOOST_FOREACH(InputDevice* device, devices_)
386            if (device != NULL)
387                device->update(time);
388
389        // Collect function calls for the update
390        for (unsigned int i = 0; i < activeStatesTicked_.size(); ++i)
391            activeStatesTicked_[i]->update(time.getDeltaTime());
392
393        // Execute all cached function calls in order
394        // Why so complicated? The problem is that an InputHandler could trigger
395        // a reload that would destroy the OIS devices or it could even leave and
396        // then destroy its own InputState. That would of course lead to access
397        // violations.
398        // If we delay the calls, then OIS and and the InputStates are not anymore
399        // in the call stack and can therefore be edited.
400        for (size_t i = 0; i < this->callBuffer_.size(); ++i)
401            this->callBuffer_[i]();
402
403        this->callBuffer_.clear();
404    }
405
406    /**
407    @brief
408        Updates the currently active states (according to activeStates_) for each device.
409        Also, a list of all active states (no duplicates!) is compiled for the general preUpdate().
410    */
411    void InputManager::updateActiveStates()
412    {
413        // Calculate the stack of input states
414        // and assign it to the corresponding device
415        for (unsigned int i = 0; i < devices_.size(); ++i)
416        {
417            if (devices_[i] == NULL)
418                continue;
419            std::vector<InputState*>& states = devices_[i]->getStateListRef();
420            bool occupied = false;
421            states.clear();
422            for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin(); rit != activeStates_.rend(); ++rit)
423            {
424                if (rit->second->isInputDeviceEnabled(i) && (!occupied || rit->second->bAlwaysGetsInput_))
425                {
426                    states.push_back(rit->second);
427                    if (!rit->second->bTransparent_)
428                        occupied = true;
429                }
430            }
431        }
432
433        // See that we only update each InputState once for each device
434        // Using an std::set to avoid duplicates
435        std::set<InputState*> tempSet;
436        for (unsigned int i = 0; i < devices_.size(); ++i)
437            if (devices_[i] != NULL)
438                for (unsigned int iState = 0; iState < devices_[i]->getStateListRef().size(); ++iState)
439                    tempSet.insert(devices_[i]->getStateListRef()[iState]);
440
441        // Copy the content of the std::set back to the actual vector
442        activeStatesTicked_.clear();
443        for (std::set<InputState*>::const_iterator it = tempSet.begin();it != tempSet.end(); ++it)
444            activeStatesTicked_.push_back(*it);
445
446        // Check whether we have to change the mouse mode
447        TriBool::Value requestedMode = TriBool::Dontcare;
448        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
449        if (mouseStates.empty())
450            requestedMode = TriBool::False;
451        else
452            requestedMode = mouseStates.front()->getMouseExclusive();
453        if (requestedMode != TriBool::Dontcare && exclusiveMouse_ != requestedMode)
454        {
455            exclusiveMouse_ = requestedMode;
456            if (!GraphicsManager::getInstance().isFullScreen())
457                this->reloadInternal();
458        }
459    }
460
461    void InputManager::clearBuffers()
462    {
463        BOOST_FOREACH(InputDevice* device, devices_)
464            if (device != NULL)
465                device->clearBuffers();
466    }
467
468    void InputManager::calibrate()
469    {
470        COUT(0) << "Move all joy stick axes fully in all directions." << std::endl
471                << "When done, put the axex in the middle position and press enter." << std::endl;
472
473        BOOST_FOREACH(InputDevice* device, devices_)
474            if (device != NULL)
475                device->startCalibration();
476
477        internalState_ |= Calibrating;
478        enterState("calibrator");
479    }
480
481    //! Tells all devices to stop the calibration and evaluate it. Buffers are being cleared as well!
482    void InputManager::stopCalibration()
483    {
484        BOOST_FOREACH(InputDevice* device, devices_)
485            if (device != NULL)
486                device->stopCalibration();
487
488        // restore old input state
489        leaveState("calibrator");
490        internalState_ &= ~Calibrating;
491        // Clear buffers to prevent button hold events
492        this->clearBuffers();
493
494        COUT(0) << "Calibration has been stored." << std::endl;
495    }
496
497    //! Gets called by WindowEventListener upon focus change --> clear buffers
498    void InputManager::windowFocusChanged(bool bFocus)
499    {
500        this->clearBuffers();
501    }
502
503    std::pair<int, int> InputManager::getMousePosition() const
504    {
505        Mouse* mouse = static_cast<Mouse*>(devices_[InputDeviceEnumerator::Mouse]);
506        if (mouse != NULL)
507        {
508            const OIS::MouseState state = mouse->getOISDevice()->getMouseState();
509            return std::make_pair(state.X.abs, state.Y.abs);
510        }
511        else
512            return std::make_pair(0, 0);
513    }
514
515    // ############################################################
516    // #####                    Input States                  #####
517    // ##########                                        ##########
518    // ############################################################
519
520    InputState* InputManager::createInputState(const std::string& name, bool bAlwaysGetsInput, bool bTransparent, InputStatePriority priority)
521    {
522        if (name.empty())
523            return 0;
524        if (statesByName_.find(name) == statesByName_.end())
525        {
526            if (priority >= InputStatePriority::HighPriority || priority == InputStatePriority::Empty)
527            {
528                // Make sure we don't add two high priority states with the same priority
529                for (std::map<std::string, InputState*>::const_iterator it = this->statesByName_.begin();
530                    it != this->statesByName_.end(); ++it)
531                {
532                    if (it->second->getPriority() == priority)
533                    {
534                        COUT(2) << "Warning: Could not add an InputState with the same priority '"
535                            << static_cast<int>(priority) << "' != 0." << std::endl;
536                        return 0;
537                    }
538                }
539            }
540            InputState* state = new InputState(name, bAlwaysGetsInput, bTransparent, priority);
541            statesByName_[name] = state;
542
543            return state;
544        }
545        else
546        {
547            COUT(2) << "Warning: Could not add an InputState with the same name '" << name << "'." << std::endl;
548            return 0;
549        }
550    }
551
552    InputState* InputManager::getState(const std::string& name)
553    {
554        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
555        if (it != statesByName_.end())
556            return it->second;
557        else
558            return 0;
559    }
560
561    bool InputManager::enterState(const std::string& name)
562    {
563        // get pointer from the map with all stored handlers
564        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
565        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) == activeStates_.end())
566        {
567            // exists and not active
568            if (it->second->getPriority() == 0)
569            {
570                // Get smallest possible priority between 1 and maxStateStackSize_s
571                for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
572                    rit != activeStates_.rend(); ++rit)
573                {
574                    if (rit->first < InputStatePriority::HighPriority)
575                    {
576                        it->second->setPriority(rit->first + 1);
577                        break;
578                    }
579                }
580                // In case no normal handler was on the stack
581                if (it->second->getPriority() == 0)
582                    it->second->setPriority(1);
583            }
584            activeStates_[it->second->getPriority()] = it->second;
585            updateActiveStates();
586            it->second->entered();
587
588            return true;
589        }
590        return false;
591    }
592
593    bool InputManager::leaveState(const std::string& name)
594    {
595        if (name == "empty")
596        {
597            COUT(2) << "InputManager: Leaving the empty state is not allowed!" << std::endl;
598            return false;
599        }
600        // get pointer from the map with all stored handlers
601        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
602        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) != activeStates_.end())
603        {
604            // exists and active
605
606            it->second->left();
607
608            activeStates_.erase(it->second->getPriority());
609            if (it->second->getPriority() < InputStatePriority::HighPriority)
610                it->second->setPriority(0);
611            updateActiveStates();
612
613            return true;
614        }
615        return false;
616    }
617
618    bool InputManager::destroyState(const std::string& name)
619    {
620        if (name == "empty")
621        {
622            COUT(2) << "InputManager: Removing the empty state is not allowed!" << std::endl;
623            return false;
624        }
625        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
626        if (it != statesByName_.end())
627        {
628            this->leaveState(name);
629            destroyStateInternal(it->second);
630
631            return true;
632        }
633        return false;
634    }
635
636    //! Destroys an InputState internally.
637    void InputManager::destroyStateInternal(InputState* state)
638    {
639        assert(state && this->activeStates_.find(state->getPriority()) == this->activeStates_.end());
640        statesByName_.erase(state->getName());
641        state->destroy();
642    }
643}
Note: See TracBrowser for help on using the repository browser.