Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/wiimote/src/libraries/core/input/InputManager.cc @ 9723

Last change on this file since 9723 was 9723, checked in by georgr, 11 years ago

New way to output insulting messages \o/

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