Merge pull request #206 from kyzentun/InputCallbacks

This seems solid in my testing, I've converted Consensual to use it instead of CodeMessageCommand.
This commit is contained in:
kyzentun
2014-07-03 23:15:03 -06:00
9 changed files with 204 additions and 1 deletions
+12
View File
@@ -1,4 +1,5 @@
#include "global.h"
#include "LocalizedString.h"
#include "InputFilter.h"
#include "RageLog.h"
#include "RageInput.h"
@@ -11,6 +12,17 @@
#include "ScreenDimensions.h"
#include <set>
static const char *InputEventTypeNames[] = {
"FirstPress",
"Repeat",
"Release"
};
XToString(InputEventType);
XToLocalizedString(InputEventType);
LuaXType(InputEventType);
struct ButtonState
{
ButtonState();
+4
View File
@@ -22,6 +22,10 @@ enum InputEventType
InputEventType_Invalid
};
const RString& InputEventTypeToString(InputEventType cat);
const RString& InputEventTypeToLocalizedString(InputEventType cat);
LuaDeclareType(InputEventType);
struct InputEvent
{
InputEvent(): type(IET_FIRST_PRESS) {}
+7
View File
@@ -17,6 +17,13 @@ public:
LuaReference( const LuaReference &cpy );
LuaReference &operator=( const LuaReference &cpy );
// Convenience constructor.
LuaReference(Lua *L)
:m_iReference(LUA_NOREF)
{
SetFromStack(L);
}
/* Create a reference pointing to the item at the top of the stack, and pop
* the stack. */
void SetFromStack( Lua *L );
+2
View File
@@ -15,6 +15,7 @@ static const char *InputDeviceStateNames[] = {
};
XToString( InputDeviceState );
XToLocalizedString( InputDeviceState );
LuaXType(InputDevice);
static map<DeviceButton,RString> g_mapNamesToString;
static map<RString,DeviceButton> g_mapStringToNames;
@@ -190,6 +191,7 @@ DeviceButton StringToDeviceButton( const RString& s )
return DeviceButton_Invalid;
}
LuaXType(DeviceButton);
static const char *InputDeviceNames[] = {
"Key",
+126
View File
@@ -7,6 +7,7 @@
#include "ScreenManager.h"
#include "ActorUtil.h"
#include "InputEventPlus.h"
#include "InputMapper.h"
#define NEXT_SCREEN THEME->GetMetric (m_sName,"NextScreen")
#define PREV_SCREEN THEME->GetMetric (m_sName,"PrevScreen")
@@ -54,6 +55,8 @@ void Screen::Init()
m_smSendOnPop = SM_None;
m_bRunning = false;
m_CallingInputCallbacks= false;
ActorUtil::LoadAllCommandsFromName( *this, m_sName, "Screen" );
PlayCommandNoRecurse( Message("Init") );
@@ -284,6 +287,107 @@ void Screen::ClearMessageQueue( const ScreenMessage SM )
m_QueuedMessages.erase( m_QueuedMessages.begin()+i );
}
bool Screen::PassInputToLua(const InputEventPlus& input)
{
if(m_InputCallbacks.empty())
{
return false;
}
m_CallingInputCallbacks= true;
bool handled= false;
Lua* L= LUA->Get();
// Construct the table once, and reuse it.
lua_createtable(L, 0, 7);
{ // This block is meant to improve clarity. A subtable is created for
// storing the DeviceInput member.
lua_createtable(L, 0, 8);
Enum::Push(L, input.DeviceI.device);
lua_setfield(L, -2, "device");
Enum::Push(L, input.DeviceI.button);
lua_setfield(L, -2, "button");
lua_pushnumber(L, input.DeviceI.level);
lua_setfield(L, -2, "level");
lua_pushinteger(L, input.DeviceI.z);
lua_setfield(L, -2, "z");
lua_pushboolean(L, input.DeviceI.bDown);
lua_setfield(L, -2, "down");
lua_pushnumber(L, input.DeviceI.ts.Ago());
lua_setfield(L, -2, "ago");
lua_pushboolean(L, input.DeviceI.IsJoystick());
lua_setfield(L, -2, "is_joystick");
lua_pushboolean(L, input.DeviceI.IsMouse());
lua_setfield(L, -2, "is_mouse");
}
lua_setfield(L, -2, "DeviceInput");
Enum::Push(L, input.GameI.controller);
lua_setfield(L, -2, "controller");
LuaHelpers::Push(L, GameButtonToString(INPUTMAPPER->GetInputScheme(), input.GameI.button));
lua_setfield(L, -2, "button");
Enum::Push(L, input.type);
lua_setfield(L, -2, "type");
LuaHelpers::Push(L, GameButtonToString(INPUTMAPPER->GetInputScheme(), input.MenuI));
lua_setfield(L, -2, "GameButton");
Enum::Push(L, input.pn);
lua_setfield(L, -2, "PlayerNumber");
Enum::Push(L, input.mp);
lua_setfield(L, -2, "MultiPlayer");
for(map<callback_key_t, LuaReference>::iterator callback= m_InputCallbacks.begin();
callback != m_InputCallbacks.end() && !handled; ++callback)
{
callback->second.PushSelf(L);
lua_pushvalue(L, -2);
RString error;
if(!LuaHelpers::RunScriptOnStack(L, error, 1, 1))
{
LOG->Warn("Error running input callback: %s", error.c_str());
}
handled= lua_toboolean(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
LUA->Release(L);
m_CallingInputCallbacks= false;
if(!m_DelayedCallbackRemovals.empty())
{
for(vector<callback_key_t>::iterator key= m_DelayedCallbackRemovals.begin();
key != m_DelayedCallbackRemovals.end(); ++key)
{
InternalRemoveCallback(*key);
}
}
return handled;
}
void Screen::AddInputCallbackFromStack(lua_State* L)
{
callback_key_t key= lua_topointer(L, -1);
m_InputCallbacks[key]= LuaReference(L);
}
void Screen::RemoveInputCallback(lua_State* L)
{
callback_key_t key= lua_topointer(L, -1);
if(m_CallingInputCallbacks)
{
m_DelayedCallbackRemovals.push_back(key);
}
else
{
InternalRemoveCallback(key);
}
}
void Screen::InternalRemoveCallback(callback_key_t key)
{
map<callback_key_t, LuaReference>::iterator iter= m_InputCallbacks.find(key);
if(iter != m_InputCallbacks.end())
{
m_InputCallbacks.erase(iter);
}
}
// lua start
#include "LuaBinding.h"
@@ -304,6 +408,26 @@ public:
return 0;
}
static int AddInputCallback(T* p, lua_State* L)
{
if(!lua_isfunction(L, -1))
{
luaL_error(L, "Input callback must be a function.");
}
p->AddInputCallbackFromStack(L);
return 0;
}
static int RemoveInputCallback(T* p, lua_State* L)
{
if(!lua_isfunction(L, -1))
{
luaL_error(L, "Input callback must be a function.");
}
p->RemoveInputCallback(L);
return 0;
}
LunaScreen()
{
ADD_METHOD( GetNextScreenName );
@@ -311,6 +435,8 @@ public:
ADD_METHOD( PostScreenMessage );
ADD_METHOD( lockinput );
ADD_METHOD( GetScreenType );
ADD_METHOD( AddInputCallback );
ADD_METHOD( RemoveInputCallback );
}
};
+13
View File
@@ -124,6 +124,10 @@ public:
RString GetNextScreenName() const;
RString GetPrevScreen() const;
bool PassInputToLua(const InputEventPlus& input);
void AddInputCallbackFromStack(lua_State* L);
void RemoveInputCallback(lua_State* L);
// let subclass override if they want
virtual bool MenuUp(const InputEventPlus &) { return false; }
virtual bool MenuDown(const InputEventPlus &) { return false; }
@@ -139,6 +143,15 @@ public:
//virtual bool MiddleClick(const InputEventPlus &) { }
//virtual bool MouseWheelUp(const InputEventPlus &) { }
//virtual bool MouseWheelDown(const InputEventPlus &) { }
private:
// void* is the key so that we can use lua_topointer to find the callback
// to remove when removing a callback.
typedef void const* callback_key_t;
map<callback_key_t, LuaReference> m_InputCallbacks;
vector<callback_key_t> m_DelayedCallbackRemovals;
bool m_CallingInputCallbacks;
void InternalRemoveCallback(callback_key_t key);
};
#endif
+6 -1
View File
@@ -509,7 +509,11 @@ void ScreenManager::Input( const InputEventPlus &input )
for( unsigned i = 0; i < g_OverlayScreens.size(); ++i )
{
Screen *pScreen = g_OverlayScreens[i];
if( pScreen->Input(input) )
bool handled= pScreen->Input(input);
// Pass input to the screen and lua. Contention shouldn't be a problem
// because anybody setting an input callback is probably doing it to
// do something in addition to whatever the screen does.
if(pScreen->PassInputToLua(input) || handled)
return;
}
@@ -522,6 +526,7 @@ void ScreenManager::Input( const InputEventPlus &input )
return;
g_ScreenStack.back().m_pScreen->Input( input );
g_ScreenStack.back().m_pScreen->PassInputToLua( input );
}
// Just create a new screen; don't do any associated cleanup.