[PATCH INCLUDED] Setting events to Lua members.

For help with anything that CEGUI doesn't offer straight out-of-the-box, e.g.:
- Implementation of new features, such as new Core classes, widgets, WindowRenderers, etc. ...
- Modification of any existing features for specific purposes
- Integration of CEGUI in new engines or frameworks and writing of new plugins (Renderer, Parser, ...) or modules

Moderators: CEGUI MVP, CEGUI Team

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

[PATCH INCLUDED] Setting events to Lua members.

Postby kiolakin » Wed Aug 02, 2006 00:56

Ok, finally got it rewritten. Much tighter IMO. And because there
is no more redundant code; it is faster, uses less memory,
and will handle more possibilities than I had originally considered.

It was alot easier to test as well, as a single call will basically chain
through every part.

Here is the patch I added to my cegui 4

I didn't include an actual diff for 2 reasons:
1. I need to find a good diff program for windows.
2. I think I am the only one who will probably do this for version 4,
but once again, if its not in a newer version, I would like to see it.

This allows you to:
myWindow:subscribeEvent("Clicked","my:class:chain:to:the:function")

It also works with tolua (userdata) objects by doing the metatable
lookups.

CEGUILua.h - in the private: section

Code: Select all

   /*! Takes away syntacitcal sugar. (!!Warning!!! will change the string you pass in!)*/
   void lua_desugar(String &call_object);
   /*! Pushes an argument list onto the stack...or just comma seperated variables.
      \return
      Number of variables processed (or parameters to a call).
   */
   int Olua_pushvariables(lua_State *L,const char *args);
   /*! Uses all methods to get at a table value for a pointer.
      \return
      Table index to use. (global or local)
   */
   int Olua_pushtableof(lua_State *L,const char *ptr,int table_index);
   /*! Pushes the value of any variable (global or member) onto the stack. */
   void Olua_pushanyvariable(lua_State *L,const char *var);
   /*! Pushes the value of any function (global or member), ready for a call. */
   int Olua_pushanyfunction(lua_State *L,const char *var);
   /*! Pushes any function, and self if need be onto the stack.
      \return
      Number of args generated (did it push self for you)
   */
   int Olua_setupcall(lua_State *L,String call);


CEGUILua.cpp - the modified executeScriptedEvent

Code: Select all

bool LuaScriptModule::executeScriptedEventHandler(const String& handler_name, const EventArgs& e)
   {
   try
   {
      int nargs=Olua_setupcall(d_state,handler_name)+1;

      // push EventArgs as the first parameter
      tolua_pushusertype(d_state,(void*)&e,"const CEGUI::EventArgs");

      // call it
      int error = lua_pcall(d_state,nargs,0,0);

      // handle errors
      if ( error )
      {
         String msg = lua_tostring(d_state,-1);
         lua_pop(d_state,1);
         throw msg;
      }

      // return it
      return true;
   }

   catch( const String& str )
   {
      lua_settop( d_state, 0 );
      String msg = "(LuaScriptModule) Unable to execute scripted event handler: "+handler_name+"\n\n"+str+"\n";
      throw GenericException( msg );
   }

   return false;

}


CEGUILua.cpp(same file) The function implementation additions.

Code: Select all

/** Takes away syntacitcal sugar. (!!Warning!!! will change the string you pass in!)*/
void LuaScriptModule::lua_desugar(String &call_object)
{
   int pos;

   //Take away any syntactical sugar
   do {
      pos=call_object.find(":");
      if(pos!=String::npos)
         call_object[pos]='.';
   } while(pos!=String::npos);

}

/** Pushes an argument list onto the stack...or just comma seperated variables.
   \return
   Number of variables processed (or parameters to a call).
*/
int LuaScriptModule::Olua_pushvariables(lua_State *L,const char *args)
{
   int pos,str_i;
   String var;

   String s_str(args);

   int nargs=0;
   if(s_str.length())                     //Deal with each parameter
   {
      pos=-1;
      while(pos != s_str.length())
      {
         str_i=pos+1;
         pos=s_str.find(",",str_i);
         if(pos==String::npos)
            pos=s_str.length();
         else
            pos--;

         //Pop off top of the list
         var=s_str.substr(str_i,pos-str_i);
     
         //Push the value of it on the stack
         Olua_pushanyvariable(L,var.c_str());

         nargs++;
      }
   }

   return nargs;
}

/** Uses all methods to get at a table value for a pointer.
   \return
   Table index to use. (global or local)
*/
int LuaScriptModule::Olua_pushtableof(lua_State *L,const char *ptr,int table_index)
{
   bool done=false;
   int pos,error,nargs;
   String name,params;

   int ret_val=1;            // Everyone but metamethods should return a ptr
   String s_ptr(ptr);                 
   
   while(!done)
   {
      pos=s_ptr.find("(");                     //If it is a function, keep the
      if(pos==String::npos)                  //parameters out of the call.
         pos=s_ptr.length();

      //If this is empty, the value we need
      //is already on the stack.
      if(!s_ptr.empty())
      {
         name=s_ptr.substr(0,pos);
         lua_pushstring(L,(const char *)name.c_str());   //Push this onto the stack
         lua_gettable(L,table_index);               //Get its value.
      }

      switch(lua_type(L,-1))                     //Determine what type this is.
      {
      case LUA_TTABLE:                        //It is a table, we got what we needed.
      if(table_index != LUA_GLOBALSINDEX)     // Remove the old table.
         lua_remove(L,table_index);
         done=true;
         table_index=-2;
         break;
      case LUA_TUSERDATA:                        //A C-ptr, probably has a metamethod.
         if(!lua_getmetatable(L,-1))
            throw(String("Syntax Error:"
               +s_ptr
               +" has no table."));
         s_ptr="__index()";                     //Now we just want this function's
         table_index=-2;                        //return value.
         ret_val=0;                           //Metamethods dont return anything :/
         break;
      case LUA_TFUNCTION:
         params=s_ptr.substr(pos+1,
            s_ptr.length()-pos-2);
         nargs=Olua_pushvariables(L,               //Push the parameters.
            params.c_str());                     
           
         error=lua_pcall(L,nargs,ret_val,0);         //Call the function.
         s_ptr="";
         ret_val=1;                           //Make sure we reset this.

         if(error)
            throw(String("Parameters to "
               +s_ptr+" are not valid."));
         break;
      default:                              //?No pointer?
         throw(String
            ("Syntax Error: "
            +s_ptr
            +" is not a pointer to a known member."));
      };
   }

   return table_index;
}

/** Pushes the value of any variable (global or member) onto the stack. */
void LuaScriptModule::Olua_pushanyvariable(lua_State *L,const char *var)
{
   int str_i=0;
   String token;

   String call(var);

   // Start at the global table.
   int table_index=LUA_GLOBALSINDEX;

   // Do one part at a time.
   int pos=call.find(".",str_i);
   while(pos != String::npos)
   {
      // Get a table value
      token=call.substr(str_i,pos-str_i);
      table_index=Olua_pushtableof(L,token.c_str(),table_index);
     
      str_i=pos+1;
      pos=call.find(".",str_i);
     
   }

   //Push the variable on the stack and get the variable value
   token=call.substr(str_i);
   lua_pushstring(L,token.c_str());
   lua_gettable(L,table_index);
   if(table_index != LUA_GLOBALSINDEX)
      lua_remove(L,table_index);
}

/** Pushes the value of any function (global or member), ready for a call. */
int LuaScriptModule::Olua_pushanyfunction(lua_State *L,const char *var)
{
   int str_i=0;
   String token;

   String call(var);

   // Start at the global table.
   int table_index=LUA_GLOBALSINDEX;

   // Do one part at a time.
   int pos=call.find(".",str_i);
   while(pos != String::npos)
   {
      // Get a table value
      token=call.substr(str_i,pos-str_i);
      table_index=Olua_pushtableof(L,token.c_str(),table_index);
     
      str_i=pos+1;
      pos=call.find(".",str_i);
     
   }

   //Push the function on the stack and get the function value
   token=call.substr(str_i);
   lua_pushstring(L,token.c_str());
   lua_gettable(L,table_index);
   if(table_index != LUA_GLOBALSINDEX)
      lua_remove(L,table_index);

   //Don't push self.
   if(table_index==LUA_GLOBALSINDEX)
      return 0;

   //Push self.
   return 1;
}

/** Pushes any function, and self if need be onto the stack.
   \return
   Number of args generated (did it push self for you)
*/
int LuaScriptModule::Olua_setupcall(lua_State *L,String call)
{
   String token;
   int str_i=0;

   // Get rid of syntactical sugar.
   lua_desugar(call);

   // Put the function on the stack.
   int nargs=Olua_pushanyfunction(L,call.c_str());

   // If this is a member call, push self
   if(nargs)   
   {
      token=call.substr(0,call.find_last_of("."));
      Olua_pushvariables(L,token.c_str());
   }
     
   return nargs;
}


Anyway, hope someone else gets some use out of this.

Edit: To fix a problem with long calls.
Last edited by kiolakin on Mon Aug 07, 2006 14:26, edited 1 time in total.

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Sat Aug 05, 2006 02:39

No response for no interest, already implemented, or just not enough time to look at it yet ?

That little piece of code gave me a headache for 3 days :p
I almost choked when I had to go back and rewrite it clean.

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Sat Aug 05, 2006 05:20

Ok I just downloaded 5.0RC1 to see if it was implemented already. It is almost there..and your code is much nicer than mine :p

But you should still take into account userdata. The way that I am using CEGUI is as a scriptable interface in my game, but there are times when someone will want to hook into a C-function that I tolua out. That function will not show up as a function, it will show up as userdata the minute they invoke the class pointer.

The above code takes into account both lua classes and exported c++ classes.

I actually thought about patching it for you but I don't want to hack up your pretty code with mine. :/

User avatar
lindquist
CEGUI Team (Retired)
Posts: 770
Joined: Mon Jan 24, 2005 21:20
Location: Copenhagen, Denmark

Postby lindquist » Sat Aug 05, 2006 06:41

sry about the duplicated effort here. your features seem interesting. When I have time I'll take a closer look and probably add some of them to CEGUILua.

the detection of metatables is nice :)

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Sat Aug 05, 2006 17:47

Score!!!! After all the headaches that code caused me, I was starting to think noone cared. :( Metatables are wierd, they push a value on the stack, but technically don't return anything. The above never would have worked if I didn't slip up once trying to debug and put a 0 where a 1 should have been.


To put it in as is, if you want to see it work, just call

Olua_pushtableof()

where you want to push a table...it will push a global table, retrieve a pointer from a function and get that table, or it will run through the metatables to find the table..it recurses until it actually finds a table to push.

It returns 1 if you need to push self, or 0 if it was a global. Basically at the point where you are recursing in functor you could replace with this. If there is a function in the middle of a call like:

myclass:functionreturnsclass(value:variable,value:function):anotherclass:function

It will also use pushtableof on the variables for that function, get the pointer it returns, and attempt to find a metatable for that userdata..or if its a lua function that just returns a table, it will take that as well.

So then, to actually push self, it amounts to just calling Olua_pushtableof() for self in your table push loop.

The Olua_pushanyvariable is just for pushing variables, it is called by Olua_pushtableof when there is a function mid chain that needs parameters, just in case the parameters aren't global. It can also be used to recurse down self and push it for you.

Olua_pushvariables just calls Olua_pushanyvariable for each variable in a comma seperated list. It's just a convenience for parameter lists. It also returns the number of variables it pushed.

The Olua_setupcall just does all of the above for you, so you are only left to push your variables and call the function. Olua_setupcall will also return 1 if it pushed self for you, or 0 if it was a global call, but if you want to keep your functor functionality, you probably wouldn't use that one. Looking at the 3-4 lines in Olua_setupcall is a good way to see how the rest of it works though.

Edit: Once I remembered my own code a little better.

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Sat Aug 05, 2006 18:17

lindquist wrote:sry about the duplicated effort here.


Well, it is technically not a duplicated effort, so don't be sorry about that. :)

My need for this stemmed from wanting to be able to have a Lua defined gui. But since my key map window will also be in lua, I needed a way to bind OIS::Keypressed to lua events. Of course I am not going to let players have at all my data structures, so that means I had to be able to allow my keypress events to be called in lua but have the option to call C++ functions in the class I give them a global variable to.

So either way, I needed to write this for OIS, I just haven't contributed much to all of these SDK's Im using, so I am trying to pitch in.

(Next week I am going to attempt to modify mozilla/gfx and mozilla/widget to support CEGUI :D ... That may take awhile though.)

User avatar
lindquist
CEGUI Team (Retired)
Posts: 770
Joined: Mon Jan 24, 2005 21:20
Location: Copenhagen, Denmark

Postby lindquist » Sat Aug 05, 2006 18:59

kiolakin wrote:(Next week I am going to attempt to modify mozilla/gfx and mozilla/widget to support CEGUI :D ... That may take awhile though.)


Sounds ambitious :P but interesting. Let us know how it goes :)

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Mon Aug 07, 2006 14:21

Meh, you know you coded too much on the weekend when you wake up realizing that there is a bug in code that requires you to recompile 3 libraries and to make it worse you have put the code out on the net.

Anyway, I am changing the table part of the switch to read:

Code: Select all

       case LUA_TTABLE:                        //It is a table, we got what we needed.
      if(table_index != LUA_GLOBALSINDEX)     // Remove the old table.
         lua_remove(L,table_index);
         done=true;
         table_index=-2;
         break;


because in objects longer than 2 calls, it would have left a corrupted stack.
I haven't had time to test this change yet as I had to go to work, but I am almost certain it will make the required fix.

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Mon Aug 07, 2006 15:26

lindquist wrote:
kiolakin wrote:(Next week I am going to attempt to modify mozilla/gfx and mozilla/widget to support CEGUI :D ... That may take awhile though.)


Sounds ambitious :P but interesting. Let us know how it goes :)


Well it is that time...(rolls up sleaves) I am not nearly as worried about mozilla/widget as I am mozilla/gfx. Although populating a buffer will not be near as tough as rewriting for a new platform, I have a sneaking suspicion I am about to be asked for a DeviceContext :O

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Sun Aug 13, 2006 22:05

I just patched my CEGUI 0.5 using the functions I posted above and it turned out to be almost identical to the CEGUI 0.41 change...

Code: Select all

bool LuaScriptModule::executeScriptedEventHandler(const String& handler_name, const EventArgs& e)
{
  int nargs=Olua_setupcall(d_state,handler_name.c_str())+1;

  // push EventArgs as the first parameter
  tolua_pushusertype(d_state,(void*)&e,"const CEGUI::EventArgs");

  // call it
  int error = lua_pcall(d_state,nargs+1,0,0);

  // handle errors
 if (error)
 {
     String errStr(lua_tostring(d_state,-1));
     lua_pop(d_state,1);
     throw ScriptException("Unable to evaluate the Lua event handler: '"+handler_name+"'\n\n"+errStr+"\n");
 }

 return true;
}

kiolakin
Not too shy to talk
Not too shy to talk
Posts: 22
Joined: Mon Jul 31, 2006 01:06

Postby kiolakin » Mon Jul 07, 2008 20:09

Whatever happened to this ? Was it ever looked at for inclusion? I am at the beginning of another project using CEGUI and I have to patch yet again... This is code that I can't see how you could live without if you are trying to provide scripting capability to your CEGUI.

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Postby CrazyEddie » Tue Jul 08, 2008 08:16

Hi,

I have no knowledge of this before now. Lindquist was dealing with it, and he's not active on the project at the current time. I guess there are a couple of possibilities for what happened:

1) He didn't get around to looking. or,
2) He looked and decided the modifications were not what we needed.

If you would like to repost a patch (or add one to mantis), please do so in unified diff format, and I will look it over before the next release. Note that patches in unified diff format is a requirement and not an option; any other form of submission will be rejected without a second look.

CE.

User avatar
lindquist
CEGUI Team (Retired)
Posts: 770
Joined: Mon Jan 24, 2005 21:20
Location: Copenhagen, Denmark

Postby lindquist » Wed Feb 25, 2009 08:06

I think the idea was to avoid providing *strings* to lua as event handler, the lua event subscriber take lua closures just fine, so this can easily be worked around. though it's been years, and I've probably forgotten something :)


Return to “Modifications / Integrations / Customisations”

Who is online

Users browsing this forum: No registered users and 1 guest