[Orxonox-commit 1940] r6657 - in code/branches/gamestate/src/libraries: core/input util

rgrieder at orxonox.net rgrieder at orxonox.net
Tue Mar 30 12:13:33 CEST 2010


Author: rgrieder
Date: 2010-03-30 12:13:33 +0200 (Tue, 30 Mar 2010)
New Revision: 6657

Modified:
   code/branches/gamestate/src/libraries/core/input/InputDevice.h
   code/branches/gamestate/src/libraries/core/input/InputHandler.h
   code/branches/gamestate/src/libraries/core/input/InputManager.cc
   code/branches/gamestate/src/libraries/core/input/InputManager.h
   code/branches/gamestate/src/libraries/core/input/InputState.h
   code/branches/gamestate/src/libraries/util/Singleton.h
Log:
Implemented indirect function calls for input events. It simply means that when you hit the mouse button, the resulting call stack will not include OIS nor an InputState anymore.
--> You can reload the InputManager (happens when mouse mode changes) or even modify the InputState situation (like InputManager::leaveState or even destroyState) directly now.
--> I was able to remove the indirect execution of following methods in the InputManager: leaveInputState, enterInputState, destroyInputState and reload.

Modified: code/branches/gamestate/src/libraries/core/input/InputDevice.h
===================================================================
--- code/branches/gamestate/src/libraries/core/input/InputDevice.h	2010-03-30 09:28:21 UTC (rev 6656)
+++ code/branches/gamestate/src/libraries/core/input/InputDevice.h	2010-03-30 10:13:33 UTC (rev 6657)
@@ -158,7 +158,7 @@
             // Call all the states with the held button event
             for (unsigned int iB = 0; iB < pressedButtons_.size(); ++iB)
                 for (unsigned int iS = 0; iS < inputStates_.size(); ++iS)
-                    inputStates_[iS]->buttonEvent<ButtonEvent::THold, Traits>(
+                    inputStates_[iS]->buttonEvent<ButtonEvent::THold, Traits::ButtonTypeParam>(
                         this->getDeviceID(), static_cast<DeviceClass*>(this)->getButtonEventArg(pressedButtons_[iB]));
 
             // Call states with device update events
@@ -195,7 +195,7 @@
 
             // Call states
             for (unsigned int i = 0; i < inputStates_.size(); ++i)
-                inputStates_[i]->buttonEvent<ButtonEvent::TPress, Traits>(this->getDeviceID(), static_cast<DeviceClass*>(this)->getButtonEventArg(button));
+                inputStates_[i]->buttonEvent<ButtonEvent::TPress, Traits::ButtonTypeParam>(this->getDeviceID(), static_cast<DeviceClass*>(this)->getButtonEventArg(button));
         }
 
         //! Common code for all button released events (updates pressed buttons list and calls the input states)
@@ -217,7 +217,7 @@
 
             // Call states
             for (unsigned int i = 0; i < inputStates_.size(); ++i)
-                inputStates_[i]->buttonEvent<ButtonEvent::TRelease, Traits>(this->getDeviceID(), static_cast<DeviceClass*>(this)->getButtonEventArg(button));
+                inputStates_[i]->buttonEvent<ButtonEvent::TRelease, Traits::ButtonTypeParam>(this->getDeviceID(), static_cast<DeviceClass*>(this)->getButtonEventArg(button));
         }
 
         //! Managed pointer to the OIS device

Modified: code/branches/gamestate/src/libraries/core/input/InputHandler.h
===================================================================
--- code/branches/gamestate/src/libraries/core/input/InputHandler.h	2010-03-30 09:28:21 UTC (rev 6656)
+++ code/branches/gamestate/src/libraries/core/input/InputHandler.h	2010-03-30 10:13:33 UTC (rev 6657)
@@ -111,17 +111,17 @@
     public:
         virtual ~InputHandler() { }
 
-        template<class T> void buttonEvent(unsigned int device, const T& button, ButtonEvent::TPress)
+        template<class T> void buttonEvent(unsigned int device, T button, ButtonEvent::TPress)
             { this->buttonPressed(button); }
-        template<class T> void buttonEvent(unsigned int device, const T& button, ButtonEvent::TRelease)
+        template<class T> void buttonEvent(unsigned int device, T button, ButtonEvent::TRelease)
             { this->buttonReleased(button); }
-        template<class T> void buttonEvent(unsigned int device, const T& button, ButtonEvent::THold)
+        template<class T> void buttonEvent(unsigned int device, T button, ButtonEvent::THold)
             { this->buttonHeld(button); }
-        void buttonEvent(unsigned int device, JoyStickButtonCode::ByEnum button, ButtonEvent::TPress)
+        template<> void buttonEvent<JoyStickButtonCode::ByEnum>(unsigned int device, JoyStickButtonCode::ByEnum button, ButtonEvent::TPress)
             { this->buttonPressed(device - InputDeviceEnumerator::FirstJoyStick, button); }
-        void buttonEvent(unsigned int device, JoyStickButtonCode::ByEnum button, ButtonEvent::TRelease)
+        template<> void buttonEvent<JoyStickButtonCode::ByEnum>(unsigned int device, JoyStickButtonCode::ByEnum button, ButtonEvent::TRelease)
             { this->buttonReleased(device - InputDeviceEnumerator::FirstJoyStick, button); }
-        void buttonEvent(unsigned int device, JoyStickButtonCode::ByEnum button, ButtonEvent::THold)
+        template<> void buttonEvent<JoyStickButtonCode::ByEnum>(unsigned int device, JoyStickButtonCode::ByEnum button, ButtonEvent::THold)
             { this->buttonHeld(device - InputDeviceEnumerator::FirstJoyStick, button); }
 
         virtual void buttonPressed (const KeyEvent& evt) { }

Modified: code/branches/gamestate/src/libraries/core/input/InputManager.cc
===================================================================
--- code/branches/gamestate/src/libraries/core/input/InputManager.cc	2010-03-30 09:28:21 UTC (rev 6656)
+++ code/branches/gamestate/src/libraries/core/input/InputManager.cc	2010-03-30 10:13:33 UTC (rev 6657)
@@ -94,6 +94,9 @@
 
         CCOUT(4) << "Constructing..." << std::endl;
 
+        // Allocate space for the function call buffer
+        this->callBuffer_.reserve(16);
+
         this->setConfigValues();
 
         if (GraphicsManager::getInstance().isFullScreen())
@@ -265,14 +268,19 @@
     {
         CCOUT(3) << "Destroying..." << std::endl;
 
+        // Leave all active InputStates (except "empty")
+        while (this->activeStates_.size() > 1)
+            this->leaveState(this->activeStates_.rbegin()->second->getName());
+        this->activeStates_.clear();
+
         // Destroy calibrator helper handler and state
         this->destroyState("calibrator");
         // Destroy KeyDetector and state
         calibratorCallbackHandler_->destroy();
-        // destroy the empty InputState
+        // Destroy the empty InputState
         this->destroyStateInternal(this->emptyState_);
 
-        // destroy all user InputStates
+        // Destroy all user InputStates
         while (statesByName_.size() > 0)
             this->destroyStateInternal(statesByName_.rbegin()->second);
 
@@ -334,14 +342,7 @@
 
     void InputManager::reload()
     {
-        if (internalState_ & Ticking)
-        {
-            // We cannot destroy OIS right now, because reload was probably
-            // caused by a user clicking on a GUI item. The stack trace would then
-            // include an OIS method. So it would be a very bad thing to destroy it..
-            internalState_ |= ReloadRequest;
-        }
-        else if (internalState_ & Calibrating)
+        if (internalState_ & Calibrating)
             CCOUT(2) << "Warning: Cannot reload input system. Joy sticks are currently being calibrated." << std::endl;
         else
             reloadInternal();
@@ -350,13 +351,12 @@
     //! Internal reload method. Destroys the OIS devices and loads them again.
     void InputManager::reloadInternal()
     {
-        CCOUT(3) << "Reloading ..." << std::endl;
+        CCOUT(4) << "Reloading ..." << std::endl;
 
         this->destroyDevices();
         this->loadDevices();
 
         internalState_ &= ~Bad;
-        internalState_ &= ~ReloadRequest;
         CCOUT(4) << "Reloading complete." << std::endl;
     }
 
@@ -370,70 +370,6 @@
         if (internalState_ & Bad)
             ThrowException(General, "InputManager was not correctly reloaded.");
 
-        else if (internalState_ & ReloadRequest)
-            reloadInternal();
-
-        // check for states to leave
-        if (!stateLeaveRequests_.empty())
-        {
-            for (std::set<InputState*>::iterator it = stateLeaveRequests_.begin();
-                it != stateLeaveRequests_.end(); ++it)
-            {
-                (*it)->left();
-                // just to be sure that the state actually is registered
-                assert(statesByName_.find((*it)->getName()) != statesByName_.end());
-
-                activeStates_.erase((*it)->getPriority());
-                if ((*it)->getPriority() < InputStatePriority::HighPriority)
-                    (*it)->setPriority(0);
-                updateActiveStates();
-            }
-            stateLeaveRequests_.clear();
-        }
-
-        // check for states to enter
-        if (!stateEnterRequests_.empty())
-        {
-            for (std::set<InputState*>::const_iterator it = stateEnterRequests_.begin();
-                it != stateEnterRequests_.end(); ++it)
-            {
-                // just to be sure that the state actually is registered
-                assert(statesByName_.find((*it)->getName()) != statesByName_.end());
-
-                if ((*it)->getPriority() == 0)
-                {
-                    // Get smallest possible priority between 1 and maxStateStackSize_s
-                    for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
-                        rit != activeStates_.rend(); ++rit)
-                    {
-                        if (rit->first < InputStatePriority::HighPriority)
-                        {
-                            (*it)->setPriority(rit->first + 1);
-                            break;
-                        }
-                    }
-                    // In case no normal handler was on the stack
-                    if ((*it)->getPriority() == 0)
-                        (*it)->setPriority(1);
-                }
-                activeStates_[(*it)->getPriority()] = (*it);
-                updateActiveStates();
-                (*it)->entered();
-            }
-            stateEnterRequests_.clear();
-        }
-
-        // check for states to destroy
-        if (!stateDestroyRequests_.empty())
-        {
-            for (std::set<InputState*>::iterator it = stateDestroyRequests_.begin();
-                it != stateDestroyRequests_.end(); ++it)
-            {
-                destroyStateInternal((*it));
-            }
-            stateDestroyRequests_.clear();
-        }
-
         // check whether a state has changed its EMPTY situation
         bool bUpdateRequired = false;
         for (std::map<int, InputState*>::iterator it = activeStates_.begin(); it != activeStates_.end(); ++it)
@@ -447,19 +383,27 @@
         if (bUpdateRequired)
             updateActiveStates();
 
-        // mark that we now start capturing and distributing input
-        internalState_ |= Ticking;
-
-        // Capture all the input and handle it
+        // Capture all the input and collect the function calls
+        // No event gets triggered here yet!
         BOOST_FOREACH(InputDevice* device, devices_)
             if (device != NULL)
                 device->update(time);
 
-        // Update the states
+        // Collect functions calls for the update
         for (unsigned int i = 0; i < activeStatesTicked_.size(); ++i)
             activeStatesTicked_[i]->update(time.getDeltaTime());
 
-        internalState_ &= ~Ticking;
+        // Execute all cached function calls in order
+        // Why so complicated? The problem is that an InputHandler could trigger
+        // a reload that would destroy the OIS devices or it could even leave and
+        // then destroy its own InputState. That would of course lead to access
+        // violations.
+        // If we delay the calls, then OIS and and the InputStates are not anymore
+        // in the call stack and can therefore be edited.
+        for (size_t i = 0; i < this->callBuffer_.size(); ++i)
+            this->callBuffer_[i]();
+
+        this->callBuffer_.clear();
     }
 
     /**
@@ -469,7 +413,6 @@
     */
     void InputManager::updateActiveStates()
     {
-        assert((internalState_ & InputManager::Ticking) == 0);
         // temporary resize
         for (unsigned int i = 0; i < devices_.size(); ++i)
         {
@@ -621,25 +564,30 @@
     {
         // get pointer from the map with all stored handlers
         std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
-        if (it != statesByName_.end())
+        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) == activeStates_.end())
         {
-            // exists
-            if (activeStates_.find(it->second->getPriority()) == activeStates_.end())
+            // exists and not active
+            if (it->second->getPriority() == 0)
             {
-                // not active
-                if (stateDestroyRequests_.find(it->second) == stateDestroyRequests_.end())
+                // Get smallest possible priority between 1 and maxStateStackSize_s
+                for (std::map<int, InputState*>::reverse_iterator rit = activeStates_.rbegin();
+                    rit != activeStates_.rend(); ++rit)
                 {
-                    // not scheduled for destruction
-                    // prevents a state from being added multiple times
-                    stateEnterRequests_.insert(it->second);
-                    return true;
+                    if (rit->first < InputStatePriority::HighPriority)
+                    {
+                        it->second->setPriority(rit->first + 1);
+                        break;
+                    }
                 }
+                // In case no normal handler was on the stack
+                if (it->second->getPriority() == 0)
+                    it->second->setPriority(1);
             }
-            else if (this->stateLeaveRequests_.find(it->second) != this->stateLeaveRequests_.end())
-            {
-                // State already scheduled for leaving --> cancel
-                this->stateLeaveRequests_.erase(this->stateLeaveRequests_.find(it->second));
-            }
+            activeStates_[it->second->getPriority()] = it->second;
+            updateActiveStates();
+            it->second->entered();
+
+            return true;
         }
         return false;
     }
@@ -653,20 +601,18 @@
         }
         // get pointer from the map with all stored handlers
         std::map<std::string, InputState*>::const_iterator it = statesByName_.find(name);
-        if (it != statesByName_.end())
+        if (it != statesByName_.end() && activeStates_.find(it->second->getPriority()) != activeStates_.end())
         {
-            // exists
-            if (activeStates_.find(it->second->getPriority()) != activeStates_.end())
-            {
-                // active
-                stateLeaveRequests_.insert(it->second);
-                return true;
-            }
-            else if (this->stateEnterRequests_.find(it->second) != this->stateEnterRequests_.end())
-            {
-                // State already scheduled for entering --> cancel
-                this->stateEnterRequests_.erase(this->stateEnterRequests_.find(it->second));
-            }
+            // exists and active
+
+            it->second->left();
+
+            activeStates_.erase(it->second->getPriority());
+            if (it->second->getPriority() < InputStatePriority::HighPriority)
+                it->second->setPriority(0);
+            updateActiveStates();
+
+            return true;
         }
         return false;
     }
@@ -681,19 +627,8 @@
         std::map<std::string, InputState*>::iterator it = statesByName_.find(name);
         if (it != statesByName_.end())
         {
-            if (activeStates_.find(it->second->getPriority()) != activeStates_.end())
-            {
-                // The state is still active. We have to postpone
-                stateLeaveRequests_.insert(it->second);
-                stateDestroyRequests_.insert(it->second);
-            }
-            else if (this->internalState_ & Ticking)
-            {
-                // cannot remove state while ticking
-                stateDestroyRequests_.insert(it->second);
-            }
-            else
-                destroyStateInternal(it->second);
+            this->leaveState(name);
+            destroyStateInternal(it->second);
 
             return true;
         }
@@ -703,13 +638,7 @@
     //! Destroys an InputState internally.
     void InputManager::destroyStateInternal(InputState* state)
     {
-        assert(state && !(this->internalState_ & Ticking));
-        std::map<int, InputState*>::iterator it = this->activeStates_.find(state->getPriority());
-        if (it != this->activeStates_.end())
-        {
-            this->activeStates_.erase(it);
-            updateActiveStates();
-        }
+        assert(state && this->activeStates_.find(state->getPriority()) == this->activeStates_.end());
         statesByName_.erase(state->getName());
         state->destroy();
     }

Modified: code/branches/gamestate/src/libraries/core/input/InputManager.h
===================================================================
--- code/branches/gamestate/src/libraries/core/input/InputManager.h	2010-03-30 09:28:21 UTC (rev 6656)
+++ code/branches/gamestate/src/libraries/core/input/InputManager.h	2010-03-30 10:13:33 UTC (rev 6657)
@@ -35,6 +35,7 @@
 #include <set>
 #include <string>
 #include <vector>
+#include <boost/function.hpp>
 
 #include "util/Singleton.h"
 #include "util/TriBool.h"
@@ -74,9 +75,7 @@
         {
             Nothing       = 0x00,
             Bad           = 0x02,
-            Ticking       = 0x04,
-            Calibrating   = 0x08,
-            ReloadRequest = 0x10,
+            Calibrating   = 0x04,
         };
 
         /**
@@ -170,6 +169,12 @@
         OIS::InputManager* getOISInputManager() { return this->oisInputManager_; }
         std::pair<int, int> getMousePosition() const;
 
+        //-------------------------------
+        // Function call caching
+        //-------------------------------
+        void pushCall(const boost::function<void ()>& function)
+            { this->callBuffer_.push_back(function); }
+
         static InputManager& getInstance() { return Singleton<InputManager>::getInstance(); } // tolua_export
 
     private: // functions
@@ -206,9 +211,7 @@
         std::map<int, InputState*>          activeStates_;         //!< Contains all active input states by priority (std::map is sorted!)
         std::vector<InputState*>            activeStatesTicked_;   //!< Like activeStates_, but only contains the ones that currently receive events
 
-        std::set<InputState*>               stateEnterRequests_;   //!< Requests to enter a new state
-        std::set<InputState*>               stateLeaveRequests_;   //!< Requests to leave a running state
-        std::set<InputState*>               stateDestroyRequests_; //!< Requests to destroy a state
+        std::vector<boost::function<void ()> > callBuffer_;        //!< Caches all calls from InputStates to be executed afterwards (see preUpdate)
 
         static InputManager*                singletonPtr_s;        //!< Pointer reference to the singleton
     }; // tolua_export

Modified: code/branches/gamestate/src/libraries/core/input/InputState.h
===================================================================
--- code/branches/gamestate/src/libraries/core/input/InputState.h	2010-03-30 09:28:21 UTC (rev 6656)
+++ code/branches/gamestate/src/libraries/core/input/InputState.h	2010-03-30 10:13:33 UTC (rev 6657)
@@ -34,11 +34,17 @@
 #include <cassert>
 #include <string>
 #include <vector>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
 
 #include "util/TriBool.h"
 #include "InputHandler.h"
+#include "InputManager.h"
 #include "JoyStickQuantityListener.h"
 
+#define INPUT_STATE_PUSH_CALL(deviceIndex, functionName, ...) \
+    InputManager::getInstance().pushCall(boost::function<void ()>(boost::bind(&InputHandler::functionName, handlers_[deviceIndex], __VA_ARGS__)))
+
 namespace orxonox
 {
     /**
@@ -127,8 +133,8 @@
         void update(float dt);
 
         //! Generic function that distributes all 9 button events
-        template <typename EventType, class Traits>
-        void buttonEvent(unsigned int device, const typename Traits::ButtonTypeParam button);
+        template <typename EventType, class ButtonTypeParam>
+        void buttonEvent(unsigned int device, ButtonTypeParam button);
 
         //! Event handler
         void mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize);
@@ -173,7 +179,7 @@
     {
         for (unsigned int i = 0; i < handlers_.size(); ++i)
             if (handlers_[i] != NULL)
-                handlers_[i]->allDevicesUpdated(dt);
+                INPUT_STATE_PUSH_CALL(i, allDevicesUpdated, dt);
     }
 
     FORCEINLINE void InputState::update(float dt, unsigned int device)
@@ -182,46 +188,50 @@
         {
         case InputDeviceEnumerator::Keyboard:
             if (handlers_[keyboardIndex_s] != NULL)
-                handlers_[keyboardIndex_s]->keyboardUpdated(dt);
+                INPUT_STATE_PUSH_CALL(keyboardIndex_s, keyboardUpdated, dt);
             break;
 
         case InputDeviceEnumerator::Mouse:
             if (handlers_[mouseIndex_s] != NULL)
-                handlers_[mouseIndex_s]->mouseUpdated(dt);
+                INPUT_STATE_PUSH_CALL(mouseIndex_s, mouseUpdated, dt);
             break;
 
         default: // joy sticks
             if (handlers_[device] != NULL)
-                handlers_[device]->joyStickUpdated(device - firstJoyStickIndex_s, dt);
+                INPUT_STATE_PUSH_CALL(device, joyStickUpdated, device - firstJoyStickIndex_s, dt);
             break;
         }
     }
 
-    template <typename EventType, class Traits>
-    FORCEINLINE void InputState::buttonEvent(unsigned int device, const typename Traits::ButtonTypeParam button)
+    template <typename EventType, class ButtonTypeParam>
+    FORCEINLINE void InputState::buttonEvent(unsigned int device, typename ButtonTypeParam button)
     {
         assert(device < handlers_.size());
         if (handlers_[device] != NULL)
-            handlers_[device]->buttonEvent(device, button, EventType());
+        {
+            // We have to store the function pointer to tell the compiler about its actual type because of overloading
+            void (InputHandler::*function)(unsigned int, ButtonTypeParam, EventType) = &InputHandler::buttonEvent<ButtonTypeParam>;
+            InputManager::getInstance().pushCall(boost::function<void ()>(boost::bind(function, handlers_[device], device, button, EventType())));
+        }
     }
 
     FORCEINLINE void InputState::mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize)
     {
         if (handlers_[mouseIndex_s] != NULL)
-            handlers_[mouseIndex_s]->mouseMoved(abs, rel, clippingSize);
+            INPUT_STATE_PUSH_CALL(mouseIndex_s, mouseMoved, abs, rel, clippingSize);
     }
 
     FORCEINLINE void InputState::mouseScrolled(int abs, int rel)
     {
         if (handlers_[mouseIndex_s] != NULL)
-            handlers_[mouseIndex_s]->mouseScrolled(abs, rel);
+            INPUT_STATE_PUSH_CALL(mouseIndex_s, mouseScrolled, abs, rel);
     }
 
     FORCEINLINE void InputState::joyStickAxisMoved(unsigned int device, unsigned int axis, float value)
     {
         assert(device < handlers_.size());
         if (handlers_[device] != NULL)
-            handlers_[device]->axisMoved(device - firstJoyStickIndex_s, axis, value);
+            INPUT_STATE_PUSH_CALL(device, axisMoved, device - firstJoyStickIndex_s, axis, value);
     }
 }
 

Modified: code/branches/gamestate/src/libraries/util/Singleton.h
===================================================================
--- code/branches/gamestate/src/libraries/util/Singleton.h	2010-03-30 09:28:21 UTC (rev 6656)
+++ code/branches/gamestate/src/libraries/util/Singleton.h	2010-03-30 10:13:33 UTC (rev 6657)
@@ -48,26 +48,26 @@
     {
     public:
         //! Returns a reference to the singleton instance
-        static T& getInstance()
+        FORCEINLINE static T& getInstance()
         {
             assert(T::singletonPtr_s != NULL);
             return *T::singletonPtr_s;
         }
 
         //! Tells whether the singleton has been created
-        static bool exists()
+        FORCEINLINE static bool exists()
         {
             return (T::singletonPtr_s != NULL);
         }
 
         //! Update method called by ClassSingletonManager (if used)
-        void preUpdateSingleton(const Clock& time) { static_cast<T*>(T::singletonPtr_s)->preUpdate(time); }
+        FORCEINLINE void preUpdateSingleton(const Clock& time) { static_cast<T*>(T::singletonPtr_s)->preUpdate(time); }
         //! Empty update method for the static polymorphism
-        void preUpdate(const Clock& time) { }
+        FORCEINLINE void preUpdate(const Clock& time) { }
         //! Update method called by ClassSingletonManager (if used)
-        void postUpdateSingleton(const Clock& time) { static_cast<T*>(T::singletonPtr_s)->postUpdate(time); }
+        FORCEINLINE void postUpdateSingleton(const Clock& time) { static_cast<T*>(T::singletonPtr_s)->postUpdate(time); }
         //! Empty update method for the static polymorphism
-        void postUpdate(const Clock& time) { }
+        FORCEINLINE void postUpdate(const Clock& time) { }
 
     protected:
         //! Constructor sets the singleton instance pointer




More information about the Orxonox-commit mailing list