Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/doc/src/libraries/core/input/InputManager.cc @ 7335

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

Added separate page for a commandline argument reference.
It's not too useful, but better than nothing.

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