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:
@@ -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();
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
@@ -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 );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user