Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/kicklib2/src/libraries/core/input/InputManager.cc @ 8312

Last change on this file since 8312 was 8284, checked in by rgrieder, 14 years ago

Merged revisions 7978 - 8096 from kicklib to kicklib2.

  • Property svn:eol-style set to native
File size: 24.3 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        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_ == TriBool::True || 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_ == TriBool::True || GraphicsManager::getInstance().isFullScreen())
177        {
178            if (CommandLineParser::getValue("keyboard_no_grab").getBool())
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            CCOUT(4) << "Created OIS input manager." << std::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        CCOUT(4) << "Input devices loaded." << std::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                CCOUT(2) << "Warning: Failed to create Mouse:" << ex.what() << std::endl
236                         << "Proceeding without mouse support." << std::endl;
237            }
238        }
239        else
240            CCOUT(2) << "Warning: No mouse found! Proceeding without mouse support." << std::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                CCOUT(2) << "Warning: Failed to create joy stick: " << ex.what() << std::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        CCOUT(3) << "Destroying..." << std::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        CCOUT(3) << "Destruction complete." << std::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        CCOUT(4) << "Destroying devices..." << std::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            CCOUT(4) << className << " destroyed." << std::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            COUT(1) << "OIS::InputManager destruction failed" << ex.eText << std::endl
329                    << "    Potential resource leak!" << std::endl;
330        }
331        oisInputManager_ = NULL;
332
333        internalState_ |= Bad;
334        CCOUT(4) << "Destroyed devices." << std::endl;
335    }
336
337    // ############################################################
338    // #####                     Reloading                    #####
339    // ##########                                        ##########
340    // ############################################################
341
342    void InputManager::reload()
343    {
344        if (internalState_ & Calibrating)
345            CCOUT(2) << "Warning: Cannot reload input system. Joy sticks are currently being calibrated." << std::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        CCOUT(4) << "Reloading ..." << std::endl;
354
355        this->destroyDevices();
356        this->loadDevices();
357
358        internalState_ &= ~Bad;
359        CCOUT(4) << "Reloading complete." << std::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::Value requestedMode = TriBool::Dontcare;
450        std::vector<InputState*>& mouseStates = devices_[InputDeviceEnumerator::Mouse]->getStateListRef();
451        if (mouseStates.empty())
452            requestedMode = TriBool::False;
453        else
454            requestedMode = mouseStates.front()->getMouseExclusive();
455        if (requestedMode != TriBool::Dontcare && exclusiveMouse_ != requestedMode)
456        {
457            exclusiveMouse_ = requestedMode;
458            if (!GraphicsManager::getInstance().isFullScreen())
459                this->reloadInternal();
460        }
461    }
462
463    void InputManager::clearBuffers()
464    {
465        BOOST_FOREACH(InputDevice* device, devices_)
466            if (device != NULL)
467                device->clearBuffers();
468    }
469
470    void InputManager::calibrate()
471    {
472        COUT(0) << "Move all joy stick axes fully in all directions." << std::endl
473                << "When done, put the axex in the middle position and press enter." << std::endl;
474
475        BOOST_FOREACH(InputDevice* device, devices_)
476            if (device != NULL)
477                device->startCalibration();
478
479        internalState_ |= Calibrating;
480        enterState("calibrator");
481    }
482
483    //! Tells all devices to stop the calibration and evaluate it. Buffers are being cleared as well!
484    void InputManager::stopCalibration()
485    {
486        BOOST_FOREACH(InputDevice* device, devices_)
487            if (device != NULL)
488                device->stopCalibration();
489
490        // restore old input state
491        leaveState("calibrator");
492        internalState_ &= ~Calibrating;
493        // Clear buffers to prevent button hold events
494        this->clearBuffers();
495
496        COUT(0) << "Calibration has been stored." << std::endl;
497    }
498
499    //! Gets called by WindowEventListener upon focus change --> clear buffers
500    void InputManager::windowFocusChanged(bool bFocus)
501    {
502        this->clearBuffers();
503    }
504
505    std::pair<int, int> InputManager::getMousePosition() const
506    {
507        Mouse* mouse = static_cast<Mouse*>(devices_[InputDeviceEnumerator::Mouse]);
508        if (mouse != NULL)
509        {
510            const OIS::MouseState state = mouse->getOISDevice()->getMouseState();
511            return std::make_pair(state.X.abs, state.Y.abs);
512        }
513        else
514            return std::make_pair(0, 0);
515    }
516
517    // ############################################################
518    // #####                    Input States                  #####
519    // ##########                                        ##########
520    // ############################################################
521
522    InputState* InputManager::createInputState(const std::string& name, bool bAlwaysGetsInput, bool bTransparent, InputStatePriority priority)
523    {
524        if (name.empty())
525            return 0;
526        if (statesByName_.find(name) == statesByName_.end())
527        {
528            if (priority >= InputStatePriority::HighPriority || priority == InputStatePriority::Empty)
529            {
530                // Make sure we don't add two high priority states with the same priority
531                for (std::map<std::string, InputState*>::const_iterator it = this->statesByName_.begin();
532                    it != this->statesByName_.end(); ++it)
533                {
534                    if (it->second->getPriority() == priority)
535                    {
536                        COUT(2) << "Warning: Could not add an InputState with the same priority '"
537                            << static_cast<int>(priority) << "' != 0." << std::endl;
538                        return 0;
539                    }
540                }
541            }
542            InputState* state = new InputState(name, bAlwaysGetsInput, bTransparent, priority);
543            statesByName_[name] = state;
544
545            return state;
546        }
547        else
548        {
549            COUT(2) << "Warning: Could not add an InputState with the same name '" << name << "'." << std::endl;
550            return 0;
551        }
552    }
553
554    InputState* InputManager::getState(const std::string& name)
555    {
556        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
557        if (it != statesByName_.end())
558            return it->second;
559        else
560            return 0;
561    }
562
563    bool InputManager::enterState(const std::string& name)
564    {
565        // get pointer from the map with all stored handlers
566        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
567        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) == activeStates_.end())
568        {
569            // exists and not active
570            if (it->second->getPriority() == 0)
571            {
572                // Get smallest possible priority between 1 and maxStateStackSize_s
573                for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
574                    rit != activeStates_.rend(); ++rit)
575                {
576                    if (rit->first < InputStatePriority::HighPriority)
577                    {
578                        it->second->setPriority(rit->first + 1);
579                        break;
580                    }
581                }
582                // In case no normal handler was on the stack
583                if (it->second->getPriority() == 0)
584                    it->second->setPriority(1);
585            }
586            activeStates_[it->second->getPriority()] = it->second;
587            updateActiveStates();
588            it->second->entered();
589
590            return true;
591        }
592        return false;
593    }
594
595    bool InputManager::leaveState(const std::string& name)
596    {
597        if (name == "empty")
598        {
599            COUT(2) << "InputManager: Leaving the empty state is not allowed!" << std::endl;
600            return false;
601        }
602        // get pointer from the map with all stored handlers
603        std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
604        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) != activeStates_.end())
605        {
606            // exists and active
607
608            it->second->left();
609
610            activeStates_.erase(it->second->getPriority());
611            if (it->second->getPriority() < InputStatePriority::HighPriority)
612                it->second->setPriority(0);
613            updateActiveStates();
614
615            return true;
616        }
617        return false;
618    }
619
620    bool InputManager::destroyState(const std::string& name)
621    {
622        if (name == "empty")
623        {
624            COUT(2) << "InputManager: Removing the empty state is not allowed!" << std::endl;
625            return false;
626        }
627        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
628        if (it != statesByName_.end())
629        {
630            this->leaveState(name);
631            destroyStateInternal(it->second);
632
633            return true;
634        }
635        return false;
636    }
637
638    //! Destroys an InputState internally.
639    void InputManager::destroyStateInternal(InputState* state)
640    {
641        assert(state && this->activeStates_.find(state->getPriority()) == this->activeStates_.end());
642        statesByName_.erase(state->getName());
643        state->destroy();
644    }
645
646    bool InputManager::setMouseExclusive(const std::string& name, TriBool::Value value)
647    {
648        if (name == "empty")
649        {
650            COUT(2) << "InputManager: Changing the empty state is not allowed!" << std::endl;
651            return false;
652        }
653        std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
654        if (it != statesByName_.end())
655        {
656            it->second->setMouseExclusive(value);
657            return true;
658        }
659        return false;
660    }
661}
Note: See TracBrowser for help on using the repository browser.