[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