| [1803] | 1 | /*********************************************************************** | 
|---|
 | 2 |     filename: CEGUILuaFunctor.cpp | 
|---|
 | 3 |     created:  Thu Jan 26 2006 | 
|---|
 | 4 |     author:   Tomas Lindquist Olsen <tomas@famolsen.dk> | 
|---|
 | 5 |  | 
|---|
 | 6 |     purpose:  Implementation for LuaFunctor class | 
|---|
 | 7 | *************************************************************************/ | 
|---|
 | 8 | /*************************************************************************** | 
|---|
 | 9 |  *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team | 
|---|
 | 10 |  * | 
|---|
 | 11 |  *   Permission is hereby granted, free of charge, to any person obtaining | 
|---|
 | 12 |  *   a copy of this software and associated documentation files (the | 
|---|
 | 13 |  *   "Software"), to deal in the Software without restriction, including | 
|---|
 | 14 |  *   without limitation the rights to use, copy, modify, merge, publish, | 
|---|
 | 15 |  *   distribute, sublicense, and/or sell copies of the Software, and to | 
|---|
 | 16 |  *   permit persons to whom the Software is furnished to do so, subject to | 
|---|
 | 17 |  *   the following conditions: | 
|---|
 | 18 |  * | 
|---|
 | 19 |  *   The above copyright notice and this permission notice shall be | 
|---|
 | 20 |  *   included in all copies or substantial portions of the Software. | 
|---|
 | 21 |  * | 
|---|
 | 22 |  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|---|
 | 23 |  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|---|
 | 24 |  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 
|---|
 | 25 |  *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | 
|---|
 | 26 |  *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 
|---|
 | 27 |  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 
|---|
 | 28 |  *   OTHER DEALINGS IN THE SOFTWARE. | 
|---|
 | 29 |  ***************************************************************************/ | 
|---|
 | 30 | #include "CEGUILuaFunctor.h" | 
|---|
 | 31 | #include "CEGUILogger.h" | 
|---|
 | 32 | #include "CEGUIExceptions.h" | 
|---|
 | 33 | #include "CEGUIPropertyHelper.h" | 
|---|
 | 34 |  | 
|---|
 | 35 | // include Lua libs and tolua++ | 
|---|
| [1804] | 36 | #include "lua/lua.hpp" | 
|---|
 | 37 | #include "tolua/tolua++.h" | 
|---|
| [1803] | 38 |  | 
|---|
 | 39 | // Start of CEGUI namespace section | 
|---|
 | 40 | namespace CEGUI | 
|---|
 | 41 | { | 
|---|
 | 42 |  | 
|---|
 | 43 | /************************************************************************* | 
|---|
 | 44 |     Constructor | 
|---|
 | 45 | *************************************************************************/ | 
|---|
 | 46 | LuaFunctor::LuaFunctor(lua_State* state, int func, int selfIndex) : | 
|---|
 | 47 |     L(state), | 
|---|
 | 48 |     index(func), | 
|---|
 | 49 |     self(selfIndex), | 
|---|
 | 50 |     needs_lookup(false) | 
|---|
 | 51 | { | 
|---|
 | 52 | } | 
|---|
 | 53 |  | 
|---|
 | 54 | /************************************************************************* | 
|---|
 | 55 |     Constructor | 
|---|
 | 56 | *************************************************************************/ | 
|---|
 | 57 | LuaFunctor::LuaFunctor(lua_State* state, const String& func, int selfIndex) : | 
|---|
 | 58 |     L(state), | 
|---|
 | 59 |     index(LUA_NOREF), | 
|---|
 | 60 |     self(selfIndex), | 
|---|
 | 61 |     needs_lookup(true), | 
|---|
 | 62 |     function_name(func) | 
|---|
 | 63 | { | 
|---|
 | 64 | } | 
|---|
 | 65 |  | 
|---|
 | 66 | /************************************************************************* | 
|---|
 | 67 |     Constructor | 
|---|
 | 68 | *************************************************************************/ | 
|---|
 | 69 | LuaFunctor::LuaFunctor(const LuaFunctor& cp) : | 
|---|
 | 70 |     L(cp.L), | 
|---|
 | 71 |     index(cp.index), | 
|---|
 | 72 |     self(cp.self), | 
|---|
 | 73 |     needs_lookup(cp.needs_lookup), | 
|---|
 | 74 |     function_name(cp.function_name) | 
|---|
 | 75 | { | 
|---|
 | 76 | } | 
|---|
 | 77 |  | 
|---|
 | 78 | /************************************************************************* | 
|---|
 | 79 |     Destructor | 
|---|
 | 80 | *************************************************************************/ | 
|---|
 | 81 | LuaFunctor::~LuaFunctor() | 
|---|
 | 82 | { | 
|---|
 | 83 |     if (self!=LUA_NOREF) | 
|---|
 | 84 |     { | 
|---|
 | 85 |         luaL_unref(L, LUA_REGISTRYINDEX, self); | 
|---|
 | 86 |     } | 
|---|
 | 87 |     if (index!=LUA_NOREF) | 
|---|
 | 88 |     { | 
|---|
 | 89 |         luaL_unref(L, LUA_REGISTRYINDEX, index); | 
|---|
 | 90 |     } | 
|---|
 | 91 | } | 
|---|
 | 92 |  | 
|---|
 | 93 | /************************************************************************* | 
|---|
 | 94 |     Call operator | 
|---|
 | 95 | *************************************************************************/ | 
|---|
 | 96 | bool LuaFunctor::operator()(const EventArgs& args) const | 
|---|
 | 97 | { | 
|---|
 | 98 |     // is this a late binding? | 
|---|
 | 99 |     if (needs_lookup) | 
|---|
 | 100 |     { | 
|---|
 | 101 |         pushNamedFunction(L, function_name); | 
|---|
 | 102 |         // reference function | 
|---|
 | 103 |         index = luaL_ref(L, LUA_REGISTRYINDEX); | 
|---|
 | 104 |         needs_lookup = false; | 
|---|
 | 105 |         CEGUI_LOGINSANE("Late binding of callback '"+function_name+"' performed"); | 
|---|
 | 106 |         function_name.clear(); | 
|---|
 | 107 |     } // if (needs_lookup) | 
|---|
 | 108 |  | 
|---|
 | 109 |         ScriptWindowHelper* helper = 0; | 
|---|
 | 110 |         //Set a global for this window | 
|---|
 | 111 |         if(args.d_hasWindow) | 
|---|
 | 112 |         { | 
|---|
 | 113 |                 WindowEventArgs& we = (WindowEventArgs&)args; | 
|---|
 | 114 |                 helper = new ScriptWindowHelper(we.window); | 
|---|
 | 115 |                 tolua_pushusertype(L,(void*)helper,"CEGUI::ScriptWindowHelper"); | 
|---|
 | 116 |                 lua_setglobal(L,"this"); | 
|---|
 | 117 |         } | 
|---|
 | 118 |  | 
|---|
 | 119 |     // retrieve function | 
|---|
 | 120 |     lua_rawgeti(L, LUA_REGISTRYINDEX, index); | 
|---|
 | 121 |  | 
|---|
 | 122 |     // possibly self as well? | 
|---|
 | 123 |     int nargs = 1; | 
|---|
 | 124 |     if (self != LUA_NOREF) | 
|---|
 | 125 |     { | 
|---|
 | 126 |         lua_rawgeti(L, LUA_REGISTRYINDEX, self); | 
|---|
 | 127 |         ++nargs; | 
|---|
 | 128 |     } | 
|---|
 | 129 |  | 
|---|
 | 130 |     // push EventArgs  parameter | 
|---|
 | 131 |     tolua_pushusertype(L, (void*)&args, "const CEGUI::EventArgs"); | 
|---|
 | 132 |  | 
|---|
 | 133 |     // call it | 
|---|
 | 134 |     int error = lua_pcall(L, nargs, 0, 0); | 
|---|
 | 135 |  | 
|---|
 | 136 |     // handle errors | 
|---|
 | 137 |     if (error) | 
|---|
 | 138 |     { | 
|---|
 | 139 |         String errStr(lua_tostring(L, -1)); | 
|---|
 | 140 |         lua_pop(L, 1); | 
|---|
 | 141 |                 if(helper) | 
|---|
 | 142 |                 { | 
|---|
 | 143 |                         delete helper; | 
|---|
 | 144 |                         helper = 0; | 
|---|
 | 145 |                 } | 
|---|
 | 146 |         throw ScriptException("Unable to call Lua event handler:\n\n"+errStr+"\n"); | 
|---|
 | 147 |     } // if (error) | 
|---|
 | 148 |  | 
|---|
 | 149 |         if(helper) | 
|---|
 | 150 |         { | 
|---|
 | 151 |                 delete helper; | 
|---|
 | 152 |                 helper = 0; | 
|---|
 | 153 |         } | 
|---|
 | 154 |  | 
|---|
 | 155 |     return true; | 
|---|
 | 156 | } | 
|---|
 | 157 |  | 
|---|
 | 158 | /************************************************************************* | 
|---|
 | 159 |     do event subscription by reference instead of by name | 
|---|
 | 160 | *************************************************************************/ | 
|---|
 | 161 | Event::Connection LuaFunctor::SubscribeEvent(EventSet* self, const String& event_name, int funcIndex, int selfIndex, lua_State* L) | 
|---|
 | 162 | { | 
|---|
 | 163 |     // should we pass a self to the callback? | 
|---|
 | 164 |     int thisIndex = LUA_NOREF; | 
|---|
 | 165 |     if (selfIndex != LUA_NOREF) | 
|---|
 | 166 |     { | 
|---|
 | 167 |         // reference self | 
|---|
 | 168 |         thisIndex = luaL_ref(L, LUA_REGISTRYINDEX); | 
|---|
 | 169 |     } | 
|---|
 | 170 |  | 
|---|
 | 171 |     // do the real subscription | 
|---|
 | 172 |     int type = lua_type(L,-1); | 
|---|
 | 173 |     Event::Connection con; | 
|---|
 | 174 |     if (type == LUA_TFUNCTION) | 
|---|
 | 175 |     { | 
|---|
 | 176 |         // reference function | 
|---|
 | 177 |         int index = luaL_ref(L, LUA_REGISTRYINDEX); | 
|---|
 | 178 |         LuaFunctor functor(L,index,thisIndex); | 
|---|
 | 179 |         con = self->subscribeEvent(String(event_name), Event::Subscriber(functor)); | 
|---|
 | 180 |         // make sure we don't release the reference(s) we just made when this call returns | 
|---|
 | 181 |         functor.index = LUA_NOREF; | 
|---|
 | 182 |         functor.self = LUA_NOREF; | 
|---|
 | 183 |     } | 
|---|
 | 184 |     else if (type == LUA_TSTRING) | 
|---|
 | 185 |     { | 
|---|
 | 186 |         const char* str = lua_tostring(L, -1); | 
|---|
 | 187 |         LuaFunctor functor(L,String(str),thisIndex); | 
|---|
 | 188 |         con = self->subscribeEvent(String(event_name), Event::Subscriber(functor)); | 
|---|
 | 189 |         // make sure we don't release the reference we just (maybe) made when this call returns | 
|---|
 | 190 |         functor.self = LUA_NOREF; | 
|---|
 | 191 |     } | 
|---|
 | 192 |     else | 
|---|
 | 193 |     { | 
|---|
 | 194 |         luaL_error(L,"bad function passed to subscribe function. must be a real function, or a string for late binding"); | 
|---|
 | 195 |     } | 
|---|
 | 196 |  | 
|---|
 | 197 |     // return the event connection | 
|---|
 | 198 |     return con; | 
|---|
 | 199 | } | 
|---|
 | 200 |  | 
|---|
 | 201 | /************************************************************************* | 
|---|
 | 202 |     Pushes a named function on the stack | 
|---|
 | 203 | *************************************************************************/ | 
|---|
 | 204 | void LuaFunctor::pushNamedFunction(lua_State* L, const String& handler_name) | 
|---|
 | 205 | { | 
|---|
 | 206 |     int top = lua_gettop(L); | 
|---|
 | 207 |  | 
|---|
 | 208 |     // do we have any dots in the handler name? if so we grab the function as a table field | 
|---|
 | 209 |     String::size_type i = handler_name.find_first_of((utf32)'.'); | 
|---|
 | 210 |     if (i!=String::npos) | 
|---|
 | 211 |     { | 
|---|
 | 212 |         // split the rest of the string up in parts seperated by '.' | 
|---|
 | 213 |         // TODO: count the dots and size the vector accordingly from the beginning. | 
|---|
 | 214 |         std::vector<String> parts; | 
|---|
 | 215 |         String::size_type start = 0; | 
|---|
 | 216 |         do | 
|---|
 | 217 |         { | 
|---|
 | 218 |             parts.push_back(handler_name.substr(start,i-start)); | 
|---|
 | 219 |             start = i+1; | 
|---|
 | 220 |             i = handler_name.find_first_of((utf32)'.',start); | 
|---|
 | 221 |         } while(i!=String::npos); | 
|---|
 | 222 |  | 
|---|
 | 223 |         // add last part | 
|---|
 | 224 |         parts.push_back(handler_name.substr(start)); | 
|---|
 | 225 |  | 
|---|
 | 226 |         // first part is the global | 
|---|
 | 227 |         lua_getglobal(L, parts[0].c_str()); | 
|---|
 | 228 |         if (!lua_istable(L,-1)) | 
|---|
 | 229 |         { | 
|---|
 | 230 |             lua_settop(L,top); | 
|---|
 | 231 |             throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as first part is not a table"); | 
|---|
 | 232 |         } | 
|---|
 | 233 |  | 
|---|
 | 234 |         // if there is more than two parts, we have more tables to go through | 
|---|
 | 235 |         std::vector<String>::size_type visz = parts.size(); | 
|---|
 | 236 |         if (visz-- > 2) // avoid subtracting one later on | 
|---|
 | 237 |         { | 
|---|
 | 238 |             // go through all the remaining parts to (hopefully) have a valid Lua function in the end | 
|---|
 | 239 |             std::vector<String>::size_type vi = 1; | 
|---|
 | 240 |             while (vi<visz) | 
|---|
 | 241 |             { | 
|---|
 | 242 |                 // push key, and get the next table | 
|---|
 | 243 |                 lua_pushstring(L,parts[vi].c_str()); | 
|---|
 | 244 |                 lua_gettable(L,-2); | 
|---|
 | 245 |                 if (!lua_istable(L,-1)) | 
|---|
 | 246 |                 { | 
|---|
 | 247 |                     lua_settop(L,top); | 
|---|
 | 248 |                     throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as part #"+PropertyHelper::uintToString(uint(vi+1))+" ("+parts[vi]+") is not a table"); | 
|---|
 | 249 |                 } | 
|---|
 | 250 |                 // get rid of the last table and move on | 
|---|
 | 251 |                 lua_remove(L,-2); | 
|---|
 | 252 |                 vi++; | 
|---|
 | 253 |             } | 
|---|
 | 254 |         } | 
|---|
 | 255 |  | 
|---|
 | 256 |         // now we are ready to get the function to call ... phew :) | 
|---|
 | 257 |         lua_pushstring(L,parts[visz].c_str()); | 
|---|
 | 258 |         lua_gettable(L,-2); | 
|---|
 | 259 |         lua_remove(L,-2); // get rid of the table | 
|---|
 | 260 |     } | 
|---|
 | 261 |     // just a regular global function | 
|---|
 | 262 |     else | 
|---|
 | 263 |     { | 
|---|
 | 264 |         lua_getglobal(L, handler_name.c_str()); | 
|---|
 | 265 |     } | 
|---|
 | 266 |  | 
|---|
 | 267 |     // is it a function | 
|---|
 | 268 |     if (!lua_isfunction(L,-1)) | 
|---|
 | 269 |     { | 
|---|
 | 270 |         lua_settop(L,top); | 
|---|
 | 271 |         throw ScriptException("The Lua event handler: '"+handler_name+"' does not represent a Lua function"); | 
|---|
 | 272 |     } | 
|---|
 | 273 | } | 
|---|
 | 274 |  | 
|---|
 | 275 | } // namespace CEGUI | 
|---|