| 1 | /*********************************************************************** | 
|---|
| 2 |         filename: CEGUILua.cpp | 
|---|
| 3 |         created:  16/3/2005 | 
|---|
| 4 |         author:   Tomas Lindquist Olsen | 
|---|
| 5 |          | 
|---|
| 6 |         purpose:  Implementation for LuaScriptModule 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 "CEGUI.h" | 
|---|
| 31 | #include "CEGUIPropertyHelper.h" | 
|---|
| 32 | #include "CEGUILua.h" | 
|---|
| 33 | #include "CEGUILuaFunctor.h" | 
|---|
| 34 | #include <vector> | 
|---|
| 35 |  | 
|---|
| 36 | // include Lua libs and tolua++ | 
|---|
| 37 | #include "lua/lua.hpp" | 
|---|
| 38 | #include "tolua/tolua++.h" | 
|---|
| 39 |  | 
|---|
| 40 | // prototype for bindings initialisation function | 
|---|
| 41 | int tolua_CEGUI_open(lua_State* tolua_S); | 
|---|
| 42 |  | 
|---|
| 43 |  | 
|---|
| 44 | // Start of CEGUI namespace section | 
|---|
| 45 | namespace CEGUI | 
|---|
| 46 | { | 
|---|
| 47 |  | 
|---|
| 48 | /************************************************************************* | 
|---|
| 49 |         Constructor (creates Lua state) | 
|---|
| 50 | *************************************************************************/ | 
|---|
| 51 | LuaScriptModule::LuaScriptModule() | 
|---|
| 52 | { | 
|---|
| 53 |     static const luaL_Reg lualibs[] = { | 
|---|
| 54 |         {"", luaopen_base}, | 
|---|
| 55 |         {LUA_LOADLIBNAME, luaopen_package}, | 
|---|
| 56 |         {LUA_TABLIBNAME, luaopen_table}, | 
|---|
| 57 |         {LUA_IOLIBNAME, luaopen_io}, | 
|---|
| 58 |         {LUA_OSLIBNAME, luaopen_os}, | 
|---|
| 59 |         {LUA_STRLIBNAME, luaopen_string}, | 
|---|
| 60 |         {LUA_MATHLIBNAME, luaopen_math}, | 
|---|
| 61 |     #if defined(DEBUG) || defined (_DEBUG) | 
|---|
| 62 |         {LUA_DBLIBNAME, luaopen_debug}, | 
|---|
| 63 |     #endif | 
|---|
| 64 |         {0, 0} | 
|---|
| 65 |     }; | 
|---|
| 66 |  | 
|---|
| 67 |     // create a lua state | 
|---|
| 68 |     d_ownsState = true; | 
|---|
| 69 |     d_state = lua_open(); | 
|---|
| 70 |  | 
|---|
| 71 |     // init all standard libraries | 
|---|
| 72 |     const luaL_Reg *lib = lualibs; | 
|---|
| 73 |     for (; lib->func; lib++) | 
|---|
| 74 |     { | 
|---|
| 75 |         lua_pushcfunction(d_state, lib->func); | 
|---|
| 76 |         lua_pushstring(d_state, lib->name); | 
|---|
| 77 |         lua_call(d_state, 1, 0); | 
|---|
| 78 |     } | 
|---|
| 79 |  | 
|---|
| 80 |     setModuleIdentifierString(); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 |  | 
|---|
| 84 | /************************************************************************* | 
|---|
| 85 |         Constructor (uses given Lua state) | 
|---|
| 86 | *************************************************************************/ | 
|---|
| 87 | LuaScriptModule::LuaScriptModule(lua_State* state) | 
|---|
| 88 | { | 
|---|
| 89 |         // just use the given state | 
|---|
| 90 |         d_ownsState = false; | 
|---|
| 91 |         d_state = state; | 
|---|
| 92 |  | 
|---|
| 93 |         setModuleIdentifierString(); | 
|---|
| 94 | } | 
|---|
| 95 |  | 
|---|
| 96 |  | 
|---|
| 97 | /************************************************************************* | 
|---|
| 98 |         Destructor | 
|---|
| 99 | *************************************************************************/ | 
|---|
| 100 | LuaScriptModule::~LuaScriptModule() | 
|---|
| 101 | { | 
|---|
| 102 |         if ( d_ownsState && d_state ) | 
|---|
| 103 |         { | 
|---|
| 104 |                 lua_close( d_state ); | 
|---|
| 105 |         } | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 |  | 
|---|
| 109 | /************************************************************************* | 
|---|
| 110 |         Execute script file | 
|---|
| 111 | *************************************************************************/ | 
|---|
| 112 | void LuaScriptModule::executeScriptFile(const String& filename, const String& resourceGroup) | 
|---|
| 113 | { | 
|---|
| 114 |         // load file | 
|---|
| 115 |         RawDataContainer raw; | 
|---|
| 116 |         System::getSingleton().getResourceProvider()->loadRawDataContainer(filename, | 
|---|
| 117 |         raw, resourceGroup.empty() ? d_defaultResourceGroup : resourceGroup); | 
|---|
| 118 |  | 
|---|
| 119 |         // load code into lua | 
|---|
| 120 |         int top = lua_gettop(d_state); | 
|---|
| 121 |         int loaderr = luaL_loadbuffer(d_state, (char*)raw.getDataPtr(), raw.getSize(), filename.c_str()); | 
|---|
| 122 |         System::getSingleton().getResourceProvider()->unloadRawDataContainer( raw ); | 
|---|
| 123 |         if (loaderr) | 
|---|
| 124 |         { | 
|---|
| 125 |             String errMsg = lua_tostring(d_state,-1); | 
|---|
| 126 |                 lua_settop(d_state,top); | 
|---|
| 127 |                 throw ScriptException("Unable to execute Lua script file: '"+filename+"'\n\n"+errMsg+"\n"); | 
|---|
| 128 |         } | 
|---|
| 129 |      | 
|---|
| 130 |     // call it | 
|---|
| 131 |         if (lua_pcall(d_state,0,0,0)) | 
|---|
| 132 |         { | 
|---|
| 133 |             String errMsg = lua_tostring(d_state,-1); | 
|---|
| 134 |                 lua_settop(d_state,top); | 
|---|
| 135 |                 throw ScriptException("Unable to execute Lua script file: '"+filename+"'\n\n"+errMsg+"\n"); | 
|---|
| 136 |         } | 
|---|
| 137 |  | 
|---|
| 138 |         lua_settop(d_state,top); // just in case :P | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 |  | 
|---|
| 142 | /************************************************************************* | 
|---|
| 143 |         Execute global script function | 
|---|
| 144 | *************************************************************************/ | 
|---|
| 145 | int     LuaScriptModule::executeScriptGlobal(const String& function_name) | 
|---|
| 146 | { | 
|---|
| 147 |     int top = lua_gettop(d_state); | 
|---|
| 148 |  | 
|---|
| 149 |     // get the function from lua | 
|---|
| 150 |     lua_getglobal(d_state, function_name.c_str()); | 
|---|
| 151 |  | 
|---|
| 152 |     // is it a function | 
|---|
| 153 |     if (!lua_isfunction(d_state,-1)) | 
|---|
| 154 |     { | 
|---|
| 155 |         lua_settop(d_state,top); | 
|---|
| 156 |         throw ScriptException("Unable to get Lua global: '"+function_name+"' as name not represent a global Lua function" ); | 
|---|
| 157 |     } | 
|---|
| 158 |  | 
|---|
| 159 |     // call it | 
|---|
| 160 |     int error = lua_pcall(d_state,0,1,0);                | 
|---|
| 161 |  | 
|---|
| 162 |     // handle errors | 
|---|
| 163 |     if (error) | 
|---|
| 164 |     { | 
|---|
| 165 |         String errMsg = lua_tostring(d_state,-1); | 
|---|
| 166 |         lua_pop(d_state,1); | 
|---|
| 167 |         throw ScriptException("Unable to evaluate Lua global: '"+function_name+"\n\n"+errMsg+"\n"); | 
|---|
| 168 |     } | 
|---|
| 169 |  | 
|---|
| 170 |     // get return value | 
|---|
| 171 |     if (!lua_isnumber(d_state,-1)) | 
|---|
| 172 |     { | 
|---|
| 173 |         // log that return value is invalid. return -1 and move on. | 
|---|
| 174 |         lua_settop(d_state,top); | 
|---|
| 175 |         ScriptException("Unable to get Lua global : '"+function_name+"' return value as it's not a number" ); | 
|---|
| 176 |         return -1; | 
|---|
| 177 |     } | 
|---|
| 178 |  | 
|---|
| 179 |     int ret = (int)lua_tonumber(d_state,-1); | 
|---|
| 180 |     lua_pop(d_state,1); | 
|---|
| 181 |  | 
|---|
| 182 |     // return it | 
|---|
| 183 |     return ret; | 
|---|
| 184 | } | 
|---|
| 185 |  | 
|---|
| 186 |  | 
|---|
| 187 | /************************************************************************* | 
|---|
| 188 |         Execute scripted event handler | 
|---|
| 189 | *************************************************************************/ | 
|---|
| 190 | bool LuaScriptModule::executeScriptedEventHandler(const String& handler_name, const EventArgs& e) | 
|---|
| 191 | { | 
|---|
| 192 |     LuaFunctor::pushNamedFunction(d_state, handler_name); | 
|---|
| 193 |  | 
|---|
| 194 |     // push EventArgs as the first parameter | 
|---|
| 195 |     tolua_pushusertype(d_state,(void*)&e,"const CEGUI::EventArgs"); | 
|---|
| 196 |  | 
|---|
| 197 |     // call it | 
|---|
| 198 |     int error = lua_pcall(d_state,1,0,0); | 
|---|
| 199 |  | 
|---|
| 200 |     // handle errors | 
|---|
| 201 |     if (error) | 
|---|
| 202 |     { | 
|---|
| 203 |         String errStr(lua_tostring(d_state,-1)); | 
|---|
| 204 |         lua_pop(d_state,1); | 
|---|
| 205 |         throw ScriptException("Unable to evaluate the Lua event handler: '"+handler_name+"'\n\n"+errStr+"\n"); | 
|---|
| 206 |     } | 
|---|
| 207 |  | 
|---|
| 208 |     return true; | 
|---|
| 209 | } | 
|---|
| 210 |  | 
|---|
| 211 |  | 
|---|
| 212 | /************************************************************************* | 
|---|
| 213 |         Execute script code string | 
|---|
| 214 | *************************************************************************/ | 
|---|
| 215 | void LuaScriptModule::executeString(const String& str) | 
|---|
| 216 | { | 
|---|
| 217 |     int top = lua_gettop(d_state); | 
|---|
| 218 |  | 
|---|
| 219 |     // load code into lua and call it | 
|---|
| 220 |     int error = luaL_loadbuffer(d_state, str.c_str(), str.length(), str.c_str()) || lua_pcall(d_state,0,0,0); | 
|---|
| 221 |  | 
|---|
| 222 |     // handle errors | 
|---|
| 223 |     if (error) | 
|---|
| 224 |     { | 
|---|
| 225 |         String errMsg = lua_tostring(d_state,-1); | 
|---|
| 226 |         lua_settop(d_state,top); | 
|---|
| 227 |         throw ScriptException("Unable to execute Lua script string: '"+str+"'\n\n"+errMsg+"\n"); | 
|---|
| 228 |     } | 
|---|
| 229 | } | 
|---|
| 230 |          | 
|---|
| 231 |  | 
|---|
| 232 | /************************************************************************* | 
|---|
| 233 |         Create Lua bindings | 
|---|
| 234 | *************************************************************************/ | 
|---|
| 235 | void LuaScriptModule::createBindings(void) | 
|---|
| 236 | { | 
|---|
| 237 |         CEGUI::Logger::getSingleton().logEvent( "---- Creating Lua bindings ----" ); | 
|---|
| 238 |         // init CEGUI module | 
|---|
| 239 |         tolua_CEGUI_open(d_state); | 
|---|
| 240 | } | 
|---|
| 241 |  | 
|---|
| 242 |  | 
|---|
| 243 | /************************************************************************* | 
|---|
| 244 |         Destroy Lua bindings | 
|---|
| 245 | *************************************************************************/ | 
|---|
| 246 | void LuaScriptModule::destroyBindings(void) | 
|---|
| 247 | { | 
|---|
| 248 |         CEGUI::Logger::getSingleton().logEvent( "---- Destroying Lua bindings ----" ); | 
|---|
| 249 |         // is this ok ? | 
|---|
| 250 |         lua_pushnil(d_state); | 
|---|
| 251 |         lua_setglobal(d_state,"CEGUI"); | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 |  | 
|---|
| 255 | /************************************************************************* | 
|---|
| 256 |         Set the ID string for the module | 
|---|
| 257 | *************************************************************************/ | 
|---|
| 258 | void LuaScriptModule::setModuleIdentifierString() | 
|---|
| 259 | { | 
|---|
| 260 |     // set ID string | 
|---|
| 261 |     d_identifierString = "CEGUI::LuaScriptModule - Official Lua based scripting module for CEGUI"; | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 |  | 
|---|
| 265 | /************************************************************************* | 
|---|
| 266 |         Subscribe to a scripted event handler | 
|---|
| 267 | *************************************************************************/ | 
|---|
| 268 | Event::Connection LuaScriptModule::subscribeEvent(EventSet* target, const String& event_name, const String& subscriber_name) | 
|---|
| 269 | { | 
|---|
| 270 |     // do the real subscription | 
|---|
| 271 |     LuaFunctor functor(d_state,subscriber_name,LUA_NOREF); | 
|---|
| 272 |     Event::Connection con = target->subscribeEvent(event_name, Event::Subscriber(functor)); | 
|---|
| 273 |     // make sure we don't release the reference we just made when this call returns | 
|---|
| 274 |     functor.index = LUA_NOREF; | 
|---|
| 275 |  | 
|---|
| 276 |     // return the event connection | 
|---|
| 277 |     return con; | 
|---|
| 278 | } | 
|---|
| 279 |  | 
|---|
| 280 |  | 
|---|
| 281 | /************************************************************************* | 
|---|
| 282 |         Subscribe to a scripted event handler | 
|---|
| 283 | *************************************************************************/ | 
|---|
| 284 | Event::Connection LuaScriptModule::subscribeEvent(EventSet* target, const String& event_name, Event::Group group, const String& subscriber_name) | 
|---|
| 285 | { | 
|---|
| 286 |     // do the real subscription | 
|---|
| 287 |     LuaFunctor functor(d_state,subscriber_name,LUA_NOREF); | 
|---|
| 288 |     Event::Connection con = target->subscribeEvent(event_name, group, Event::Subscriber(functor)); | 
|---|
| 289 |  | 
|---|
| 290 |     // return the event connection | 
|---|
| 291 |     return con; | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | } // namespace CEGUI | 
|---|