Added AddInputCallback and RemoveInputCallback to Lua API for screen. ScreenManager now passes input to the lua side of each screen in addition to the engine side.
This commit is contained in:
@@ -1271,11 +1271,13 @@
|
||||
<Class base='BitmapText' name='ScoreDisplayAliveTime'/>
|
||||
<Class base='BitmapText' name='ScoreDisplayCalories'/>
|
||||
<Class base='ActorFrame' name='Screen'>
|
||||
<Function name='AddInputCallback'/>
|
||||
<Function name='GetNextScreenName'/>
|
||||
<Function name='GetPrevScreenName'/>
|
||||
<Function name='GetScreenType'/>
|
||||
<Function name='Metric'/>
|
||||
<Function name='PostScreenMessage'/>
|
||||
<Function name='RemoveInputCallback'/>
|
||||
<Function name='String'/>
|
||||
<Function name='lockinput'/>
|
||||
</Class>
|
||||
@@ -1986,6 +1988,11 @@
|
||||
<EnumValue name=''HorizAlign_Center'' value='1'/>
|
||||
<EnumValue name=''HorizAlign_Right'' value='2'/>
|
||||
</Enum>
|
||||
<Enum name='InputEventType'>
|
||||
<EnumValue name=''InputEventType_FirstPress'' value= '0'/>
|
||||
<EnumValue name=''InputEventType_Repeat'' value= '1'/>
|
||||
<EnumValue name=''InputEventType_Release'' value= '2'/>
|
||||
</Enum>
|
||||
<Enum name='JudgmentLine'>
|
||||
<EnumValue name=''JudgmentLine_W1'' value='0'/>
|
||||
<EnumValue name=''JudgmentLine_W2'' value='1'/>
|
||||
|
||||
@@ -3576,6 +3576,30 @@ save yourself some time, copy this for undocumented things:
|
||||
</Function>
|
||||
</Class>
|
||||
<Class name='Screen'>
|
||||
<Function name='AddInputCallback' return='void' arguments='function callback'>
|
||||
This adds the lua function "callback" to the list of functions the screen will pass input to. Whenever an input event occurs, callback will be passed a table with the details of the event. callback must return a bool to indicate whether the event was handled. If callback returns true, the event will not be passed any further.<br/>
|
||||
This should not be used to provide text input because that would not handle localization or different keyboard layouts.<br/>
|
||||
The screen and the callbacks will both be passed input events, so be aware of what input the current screen responds to and consider the effects.<br/>
|
||||
Details of the table containing the event data:<br/>
|
||||
{<br/>
|
||||
DeviceInput= { -- The raw details of the event.<br/>
|
||||
device= string, -- The type of device. The first half of the string will be "Device_", the second half will be from InputDeviceNames in RageInputDevice.cpp.<br/>
|
||||
button= string, -- The button that was pressed. the first half of the string will be "DeviceButton_", the second half will be from InitNames in RageInputDevice.cpp.<br/>
|
||||
level= float, -- A floating point value for analog input.<br/>
|
||||
z= float, -- Mousewheel input.<br/>
|
||||
down= bool, -- Whether the button is down. This is level with a threshold and debouncing applied.<br/>
|
||||
ago= float, -- How long ago this input occurred, in seconds.<br/>
|
||||
is_joystick= bool, -- True if the device is a joystick.<br/>
|
||||
is_mouse= bool -- True if the device is a mouse.<br/>
|
||||
},<br/>
|
||||
controller= string, -- The game controller this event was mapped to. "GameController_1" or "GameController_2", or nil if the event wasn't mapped to either controller.
|
||||
button= string, -- The semi-raw button that was pressed. This is what the button was mapped to by the keymap settings, but without the conversions that occur when OnlyDedicatedMenuButtons is true. Will be empty if the button was not mapped.<br/>
|
||||
type= string, -- The type of event. "InputEventType_FirstPress", "InputEventType_Repeat", or "InputEventType_Release".<br/>
|
||||
GameButton= string, -- The cooked button that was pressed. This is button with mapping that occurs when OnlyDedicatedMenuButtons is true applied. This is nil for unmapped buttons.<br/>
|
||||
PlayerNumber= PlayerNumber, -- The player that the controller is mapped to, or nil.<br/>
|
||||
MultiPlayer= string, -- Unknown purpose.<br/>
|
||||
}
|
||||
</Function>
|
||||
<Function name='GetNextScreenName' return='string' arguments=''>
|
||||
Returns the name of the next Screen.
|
||||
</Function>
|
||||
@@ -3594,6 +3618,9 @@ save yourself some time, copy this for undocumented things:
|
||||
<Function name='PostScreenMessage' return='void' arguments='string sScreenMsg, float fDelay'>
|
||||
Posts a message with the text <code>sScreenMsg</code> to the Screen after <code>fDelay</code> seconds.
|
||||
</Function>
|
||||
<Function name='RemoveInputCallback' return='void' arguments='function callback'>
|
||||
This removes the callback from the list.
|
||||
</Function>
|
||||
<Function name='String' theme='_fallback' return='string' arguments='string sName'>
|
||||
[02 Other.lua] Gets a string from the current Screen in the current language.
|
||||
</Function>
|
||||
|
||||
@@ -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