/*********************************************************************** filename: CEGUILuaFunctor.cpp created: Thu Jan 26 2006 author: Tomas Lindquist Olsen purpose: Implementation for LuaFunctor class *************************************************************************/ /*************************************************************************** * Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. ***************************************************************************/ #include "CEGUILuaFunctor.h" #include "CEGUILogger.h" #include "CEGUIExceptions.h" #include "CEGUIPropertyHelper.h" // include Lua libs and tolua++ #include "lua.hpp" #include "tolua++.h" // Start of CEGUI namespace section namespace CEGUI { /************************************************************************* Constructor *************************************************************************/ LuaFunctor::LuaFunctor(lua_State* state, int func, int selfIndex) : L(state), index(func), self(selfIndex), needs_lookup(false) { } /************************************************************************* Constructor *************************************************************************/ LuaFunctor::LuaFunctor(lua_State* state, const String& func, int selfIndex) : L(state), index(LUA_NOREF), self(selfIndex), needs_lookup(true), function_name(func) { } /************************************************************************* Constructor *************************************************************************/ LuaFunctor::LuaFunctor(const LuaFunctor& cp) : L(cp.L), index(cp.index), self(cp.self), needs_lookup(cp.needs_lookup), function_name(cp.function_name) { } /************************************************************************* Destructor *************************************************************************/ LuaFunctor::~LuaFunctor() { if (self!=LUA_NOREF) { luaL_unref(L, LUA_REGISTRYINDEX, self); } if (index!=LUA_NOREF) { luaL_unref(L, LUA_REGISTRYINDEX, index); } } /************************************************************************* Call operator *************************************************************************/ bool LuaFunctor::operator()(const EventArgs& args) const { // is this a late binding? if (needs_lookup) { pushNamedFunction(L, function_name); // reference function index = luaL_ref(L, LUA_REGISTRYINDEX); needs_lookup = false; CEGUI_LOGINSANE("Late binding of callback '"+function_name+"' performed"); function_name.clear(); } // retrieve function lua_rawgeti(L, LUA_REGISTRYINDEX, index); // possibly self as well? int nargs = 1; if (self != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, self); ++nargs; } // push EventArgs parameter tolua_pushusertype(L, (void*)&args, "const CEGUI::EventArgs"); // call it int error = lua_pcall(L, nargs, 0, 0); // handle errors if (error) { String errStr(lua_tostring(L, -1)); lua_pop(L, 1); throw ScriptException("Unable to call Lua event handler:\n\n"+errStr+"\n"); } return true; } /************************************************************************* do event subscription by reference instead of by name *************************************************************************/ Event::Connection LuaFunctor::SubscribeEvent(EventSet* self, const String& event_name, int funcIndex, int selfIndex, lua_State* L) { // should we pass a self to the callback? int thisIndex = LUA_NOREF; if (selfIndex != LUA_NOREF) { // reference self thisIndex = luaL_ref(L, LUA_REGISTRYINDEX); } // do the real subscription int type = lua_type(L,-1); Event::Connection con; if (type == LUA_TFUNCTION) { // reference function int index = luaL_ref(L, LUA_REGISTRYINDEX); LuaFunctor functor(L,index,thisIndex); con = self->subscribeEvent(String(event_name), Event::Subscriber(functor)); // make sure we don't release the reference(s) we just made when this call returns functor.index = LUA_NOREF; functor.self = LUA_NOREF; } else if (type == LUA_TSTRING) { const char* str = lua_tostring(L, -1); LuaFunctor functor(L,String(str),thisIndex); con = self->subscribeEvent(String(event_name), Event::Subscriber(functor)); // make sure we don't release the reference we just (maybe) made when this call returns functor.self = LUA_NOREF; } else { luaL_error(L,"bad function passed to subscribe function. must be a real function, or a string for late binding"); } // return the event connection return con; } /************************************************************************* Pushes a named function on the stack *************************************************************************/ void LuaFunctor::pushNamedFunction(lua_State* L, const String& handler_name) { int top = lua_gettop(L); // do we have any dots in the handler name? if so we grab the function as a table field String::size_type i = handler_name.find_first_of((utf32)'.'); if (i!=String::npos) { // split the rest of the string up in parts seperated by '.' // TODO: count the dots and size the vector accordingly from the beginning. std::vector parts; String::size_type start = 0; do { parts.push_back(handler_name.substr(start,i-start)); start = i+1; i = handler_name.find_first_of((utf32)'.',start); } while(i!=String::npos); // add last part parts.push_back(handler_name.substr(start)); // first part is the global lua_getglobal(L, parts[0].c_str()); if (!lua_istable(L,-1)) { lua_settop(L,top); throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as first part is not a table"); } // if there is more than two parts, we have more tables to go through std::vector::size_type visz = parts.size(); if (visz-- > 2) // avoid subtracting one later on { // go through all the remaining parts to (hopefully) have a valid Lua function in the end std::vector::size_type vi = 1; while (vi