| 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++ | 
|---|
| 36 | #include "lua/lua.hpp" | 
|---|
| 37 | #include "tolua/tolua++.h" | 
|---|
| 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 | 
|---|