Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/external/ceguilua/ceguilua-0.6.2/ceguilua/CEGUILuaFunctor.cpp @ 7163

Last change on this file since 7163 was 6761, checked in by rgrieder, 14 years ago

Fixed "error in error handler" problem that occurred within CEGUILua scripted events (like clicking on a button).
Initialising values is always a good idea…
Also fixed a potential bug: not removing the error handler from the Lua stack can be very dangerous as I had to find out lately..

  • Property svn:eol-style set to native
File size: 13.8 KB
Line 
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 - 2008 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#include "CEGUILua.h"
35
36// include Lua libs and tolua++
37extern "C" {
38#include "lua.h"
39#include "lualib.h"
40#include "lauxlib.h"
41}
42
43#include "tolua/tolua++.h"
44
45// Start of CEGUI namespace section
46namespace CEGUI
47{
48
49/*************************************************************************
50    Constructor
51*************************************************************************/
52LuaFunctor::LuaFunctor(lua_State* state, int func, int selfIndex) :
53    L(state),
54    index(func),
55    self(selfIndex),
56    needs_lookup(false),
57    d_ourErrFuncIndex(false)
58{
59    // TODO: This would perhaps be better done another way, to avoid the
60    // TODO: interdependence.
61    LuaScriptModule* sm =
62        static_cast<LuaScriptModule*>(System::getSingleton().getScriptingModule());
63
64    if (sm)
65    {
66        d_errFuncName  = sm->getActivePCallErrorHandlerString();
67        d_errFuncIndex = sm->getActivePCallErrorHandlerReference();
68    }
69}
70
71/*************************************************************************
72    Constructor
73*************************************************************************/
74LuaFunctor::LuaFunctor(lua_State* state, const String& func, int selfIndex) :
75    L(state),
76    index(LUA_NOREF),
77    self(selfIndex),
78    needs_lookup(true),
79    function_name(func),
80    d_ourErrFuncIndex(false)
81{
82    // TODO: This would perhaps be better done another way, to avoid the
83    // TODO: interdependence.
84    LuaScriptModule* sm =
85        static_cast<LuaScriptModule*>(System::getSingleton().getScriptingModule());
86
87    if (sm)
88    {
89        d_errFuncName  = sm->getActivePCallErrorHandlerString();
90        d_errFuncIndex = sm->getActivePCallErrorHandlerReference();
91    }
92}
93
94/*************************************************************************
95    Constructor
96*************************************************************************/
97LuaFunctor::LuaFunctor(const LuaFunctor& cp) :
98    L(cp.L),
99    index(cp.index),
100    self(cp.self),
101    needs_lookup(cp.needs_lookup),
102    function_name(cp.function_name),
103    d_errFuncName(cp.d_errFuncName),
104    d_errFuncIndex(cp.d_errFuncIndex),
105    d_ourErrFuncIndex(cp.d_ourErrFuncIndex)
106{
107}
108
109/*************************************************************************
110    Destructor
111*************************************************************************/
112LuaFunctor::~LuaFunctor()
113{
114    if (self != LUA_NOREF)
115        luaL_unref(L, LUA_REGISTRYINDEX, self);
116
117    if (index != LUA_NOREF)
118        luaL_unref(L, LUA_REGISTRYINDEX, index);
119
120    if (d_ourErrFuncIndex &&
121        (d_errFuncIndex != LUA_NOREF) &&
122        !d_errFuncName.empty())
123            luaL_unref(L, LUA_REGISTRYINDEX, d_errFuncIndex);
124}
125
126/*************************************************************************
127    Call operator
128*************************************************************************/
129bool LuaFunctor::operator()(const EventArgs& args) const
130{
131    // named error handler needs binding?
132    if ((d_errFuncIndex == LUA_NOREF) && !d_errFuncName.empty())
133    {
134        pushNamedFunction(L, d_errFuncName);
135        d_errFuncIndex = luaL_ref(L, LUA_REGISTRYINDEX);
136        d_ourErrFuncIndex = true;
137    }
138
139    // is this a late binding?
140    if (needs_lookup)
141    {
142        pushNamedFunction(L, function_name);
143        // reference function
144        index = luaL_ref(L, LUA_REGISTRYINDEX);
145        needs_lookup = false;
146        CEGUI_LOGINSANE("Late binding of callback '"+function_name+"' performed");
147        function_name.clear();
148    } // if (needs_lookup)
149
150    // put error handler on stack if we're using such a thing
151    int err_idx = 0;
152    if (d_errFuncIndex != LUA_NOREF)
153    {
154        lua_rawgeti(L, LUA_REGISTRYINDEX, d_errFuncIndex);
155        err_idx = lua_gettop(L);
156    }
157
158        ScriptWindowHelper* helper = 0;
159        //Set a global for this window
160        if(args.d_hasWindow)
161        {
162                WindowEventArgs& we = (WindowEventArgs&)args;
163                helper = new ScriptWindowHelper(we.window);
164                tolua_pushusertype(L,(void*)helper,"CEGUI::ScriptWindowHelper");
165                lua_setglobal(L,"this");
166        }
167
168    // retrieve function
169    lua_rawgeti(L, LUA_REGISTRYINDEX, index);
170
171    // possibly self as well?
172    int nargs = 1;
173    if (self != LUA_NOREF)
174    {
175        lua_rawgeti(L, LUA_REGISTRYINDEX, self);
176        ++nargs;
177    }
178
179    // push EventArgs  parameter
180    tolua_pushusertype(L, (void*)&args, "const CEGUI::EventArgs");
181
182    // call it
183    int error = lua_pcall(L, nargs, 1, err_idx);
184
185    // handle errors
186    if (error)
187    {
188        String errStr(lua_tostring(L, -1));
189        lua_pop(L, 1);
190                if(helper)
191                {
192                        delete helper;
193                        helper = 0;
194                }
195        throw ScriptException("Unable to call Lua event handler:\n\n"+errStr+"\n");
196    } // if (error)
197
198    // retrieve result
199    bool ret = lua_isboolean(L, -1) ? lua_toboolean(L, -1 ) : true;
200    lua_pop(L, 1);
201
202    // remove error handler from stack
203    if (err_idx != 0)
204        lua_remove(L, err_idx);
205
206        if(helper)
207        {
208                delete helper;
209                helper = 0;
210        }
211
212    return ret;
213}
214
215/*************************************************************************
216    Pushes a named function on the stack
217*************************************************************************/
218void LuaFunctor::pushNamedFunction(lua_State* L, const String& handler_name)
219{
220    int top = lua_gettop(L);
221
222    // do we have any dots in the handler name? if so we grab the function as a table field
223    String::size_type i = handler_name.find_first_of((utf32)'.');
224    if (i!=String::npos)
225    {
226        // split the rest of the string up in parts seperated by '.'
227        // TODO: count the dots and size the vector accordingly from the beginning.
228        std::vector<String> parts;
229        String::size_type start = 0;
230        do
231        {
232            parts.push_back(handler_name.substr(start,i-start));
233            start = i+1;
234            i = handler_name.find_first_of((utf32)'.',start);
235        } while(i!=String::npos);
236
237        // add last part
238        parts.push_back(handler_name.substr(start));
239
240        // first part is the global
241        lua_getglobal(L, parts[0].c_str());
242        if (!lua_istable(L,-1))
243        {
244            lua_settop(L,top);
245            throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as first part is not a table");
246        }
247
248        // if there is more than two parts, we have more tables to go through
249        std::vector<String>::size_type visz = parts.size();
250        if (visz-- > 2) // avoid subtracting one later on
251        {
252            // go through all the remaining parts to (hopefully) have a valid Lua function in the end
253            std::vector<String>::size_type vi = 1;
254            while (vi<visz)
255            {
256                // push key, and get the next table
257                lua_pushstring(L,parts[vi].c_str());
258                lua_gettable(L,-2);
259                if (!lua_istable(L,-1))
260                {
261                    lua_settop(L,top);
262                    throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as part #"+PropertyHelper::uintToString(uint(vi+1))+" ("+parts[vi]+") is not a table");
263                }
264                // get rid of the last table and move on
265                lua_remove(L,-2);
266                vi++;
267            }
268        }
269
270        // now we are ready to get the function to call ... phew :)
271        lua_pushstring(L,parts[visz].c_str());
272        lua_gettable(L,-2);
273        lua_remove(L,-2); // get rid of the table
274    }
275    // just a regular global function
276    else
277    {
278        lua_getglobal(L, handler_name.c_str());
279    }
280
281    // is it a function
282    if (!lua_isfunction(L,-1))
283    {
284        lua_settop(L,top);
285        throw ScriptException("The Lua event handler: '"+handler_name+"' does not represent a Lua function");
286    }
287}
288
289//----------------------------------------------------------------------------//
290LuaFunctor::LuaFunctor(lua_State* state, const int func, const int selfIndex,
291    const String& error_handler) :
292    // State initialisation
293    L(state),
294    index(func),
295    self(selfIndex),
296    needs_lookup(false),
297    d_errFuncName(error_handler),
298    d_errFuncIndex(LUA_NOREF),
299    d_ourErrFuncIndex(false)
300{
301}
302
303//----------------------------------------------------------------------------//
304LuaFunctor::LuaFunctor(lua_State* state, const String& func, const int selfIndex,
305    const String& error_handler) :
306    // State initialisation
307    L(state),
308    index(LUA_NOREF),
309    self(selfIndex),
310    needs_lookup(true),
311    function_name(func),
312    d_errFuncName(error_handler),
313    d_errFuncIndex(LUA_NOREF),
314    d_ourErrFuncIndex(false)
315{
316}
317
318//----------------------------------------------------------------------------//
319LuaFunctor::LuaFunctor(lua_State* state, const int func, const int selfIndex,
320    const int error_handler) :
321    // State initialisation
322    L(state),
323    index(func),
324    self(selfIndex),
325    needs_lookup(false),
326    d_errFuncIndex(error_handler),
327    d_ourErrFuncIndex(false)
328{
329}
330
331//----------------------------------------------------------------------------//
332LuaFunctor::LuaFunctor(lua_State* state, const String& func, const int selfIndex,
333    const int error_handler) :
334    // State initialisation
335    L(state),
336    index(LUA_NOREF),
337    self(selfIndex),
338    needs_lookup(true),
339    function_name(func),
340    d_errFuncIndex(error_handler),
341    d_ourErrFuncIndex(false)
342{
343}
344
345//----------------------------------------------------------------------------//
346Event::Connection LuaFunctor::SubscribeEvent(EventSet* self,
347    const String& event_name, const int funcIndex, const int selfIndex,
348    const int error_handler, lua_State* L)
349{
350    // deal with error handler function
351    int err_idx = LUA_NOREF;
352    String err_str;
353    if (error_handler != LUA_NOREF)
354    {
355        int err_handler_type = lua_type(L,-1);
356        switch (err_handler_type)
357        {
358        case LUA_TFUNCTION:
359            // reference function
360            err_idx = luaL_ref(L, LUA_REGISTRYINDEX);
361            break;
362        case LUA_TSTRING:
363            err_str = lua_tostring(L, -1);
364            lua_pop(L, 1);
365            break;
366        default:
367            luaL_error(L, "bad error handler function passed to subscribe "
368                          "function. must be a real function, or a string for "
369                          "late binding");
370            break;
371        }
372
373    }
374
375    // should we pass a self to the callback?
376    int thisIndex = LUA_NOREF;
377    if (selfIndex != LUA_NOREF)
378    {
379        // reference self
380        thisIndex = luaL_ref(L, LUA_REGISTRYINDEX);
381    }
382
383    // do the real subscription
384    int type = lua_type(L,-1);
385    Event::Connection con;
386    if (type == LUA_TFUNCTION)
387    {
388        // reference function
389        int index = luaL_ref(L, LUA_REGISTRYINDEX);
390
391        LuaFunctor functor((err_idx != LUA_NOREF) ?
392                        LuaFunctor(L, index, thisIndex, err_idx) :
393                        (!err_str.empty()) ?
394                            LuaFunctor(L, index, thisIndex, err_str) :
395                            LuaFunctor(L, index, thisIndex));
396        con = self->subscribeEvent(String(event_name), Event::Subscriber(functor));
397        // make sure we don't release the reference(s) we just made when the
398        // 'functor' object is destroyed (goes out of scope)
399        functor.index = LUA_NOREF;
400        functor.self = LUA_NOREF;
401        functor.d_errFuncIndex = LUA_NOREF;
402    }
403    else if (type == LUA_TSTRING)
404    {
405        const char* str = lua_tostring(L, -1);
406        LuaFunctor functor((err_idx != LUA_NOREF) ?
407                        LuaFunctor(L, String(str), thisIndex, err_idx) :
408                        (!err_str.empty()) ?
409                            LuaFunctor(L, String(str), thisIndex, err_str) :
410                            LuaFunctor(L, String(str), thisIndex));
411
412        con = self->subscribeEvent(String(event_name), Event::Subscriber(functor));
413        // make sure we don't release the reference(s) we just made when the
414        // 'functor' object is destroyed (goes out of scope)
415        functor.self = LUA_NOREF;
416        functor.d_errFuncIndex = LUA_NOREF;
417    }
418    else
419    {
420        luaL_error(L, "bad function passed to subscribe function. must be a "
421                      "real function, or a string for late binding");
422    }
423
424    // return the event connection
425    return con;
426}
427
428//----------------------------------------------------------------------------//
429
430} // namespace CEGUI
Note: See TracBrowser for help on using the repository browser.