lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


hello 
 
Just wanted to say thanks to everyone that tried to help out with my question, but I still cant see the wood for the trees on this one. Its very frustrating as I managed to figure out the other api stuff without much of a problem. Full Userdata is an order of magnitude trickier.
 
I still haven't even figured out how to get the addresss/reference of the Lua side btn1:action() method
 
If anyone can spare some time to look at my rough code implementation that might get me past the impasse. I am sure the other plumbing bits I have done work, its just this action method call that is the issue.
 
If anyone can spare a bit of time to read the attached file and maybe fill in a few missing lines I would be most grateful.
Is it possible to do without using  Cclosures or lightuserdata ? No idea, but would be good if they could be avoided for reasons of simplicity.   My attachment has been snipped down a bit to make it a smaller example file.
 
Thanks Geoff
 
Date: Thu, 14 Nov 2013 13:47:53 -0500
From: mlepage@antimeta.com
To: lua-l@lists.lua.org
Subject: Re: Another userdata question

This is going to depend upon where you are storing those "action" methods.

Generally you'll need a table on the stack somewhere, then call lua_getfield with the name of the key and the stack index of the table.


If will work with a userdata if your userdata has an __index metamethod.

But again it will all depend on how you are storing this auxiliary data.

In my case, I store it in a globally available table (using a C API reference) indexed by the light userdata (the C pointer) with a table as value (containing the fields of auxiliary data). So I would be looking up that table by C pointer (light userdata) and using that when I lua_getfield. 



On Wed, Nov 13, 2013 at 8:55 PM, Geoff Smith <spammealot1@live.co.uk> wrote:




damn I am more confused on this than I thought I was :(
 
I have progressed maybe from the last post in that I have pushed my userdata instance ptr onto the stack (I used pushlightuserdata to get the fulluserdata address onto the stack)

 
but then the next line from the example is
 
 // then call the "action" method on the button object
 lua_getfield(L, -1, "action");

This only makes sense if the last thing we pushed onto the stack was a table. In my example I have just pushed a userdata address, clearly not the same thing. So what the heck was that table its looking up "action" from ?

 
I am obviously misunderstanding something here. I wish this was better documented in the official Lua docs
 
Thanks again 
 
From: spammealot1@live.co.uk

To: lua-l@lists.lua.org
Subject: RE: Another userdata question
Date: Thu, 14 Nov 2013 01:24:36 +0000




Hello 
 
Thanks for the replies, the tricky bit I am stuck on is illustrated with these 2 lines from the quoted example
 
    // and the registry to store the Lua object (this time a Lua table).

     lua_State * L = GetWindowLongPtr(hwnd, GWLP_USERDATA);
     lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)hwnd);

In my example when the instance of the userdata was created I kept a copy of the pointer to the userdata instance on the C side.  So instead of the above example where it gets the object ptr from the registry and pushes it onto the stack I was thinking I just push my stashed ptr with something like a pushUserdata() API function.

 
Only snag with my idea is no such function exists :( So how do I get my instance ptr back on the stack so that I can see if it has got an associated action function defined in the Lua code ?
 
Maybe I should stash it in and retrieve it from the Registry anyway ?

 
Thanks for any further tips. For info, I am using Lua 5.1 
 
Regards Geoff
 
Date: Wed, 13 Nov 2013 21:32:10 +0800
From: pengzhicheng1986@gmail.com

To: lua-l@lists.lua.org
Subject: Re: Another userdata question


  
    
    
  
  
    于 2013-11-13 4:48, Geoff Smith 写道:

    
    
      
      I am really hoping someone could help me with this
        question please as I am totally stuck on it for now.

         

        I am trying to implement a userdata via the C API,  to give me
        this type of functionality

         

        btn1 = graphics.newButton(x,y,width,ht, "pressMe")

        btn1:setText("Different Text")

        btn1:setFont("Arial")

         

        That's OK I think I have got that figured out and largely
        working, but the next thing I want to do is call a user defined
        Lua action function on the button instance. For example

         

        In Lua

         

        function btn1:action(someArbitrayParamBackFromC)

             print("ouch someone just poked btn1")

        end

         

        How can I call that Lua button instance function from C when I
        have detected the key is pressed ?

         

        I have done something vaguely similar in the past when I have
        created a custom timer on the Lua side, but the difference was I
        had a timer create function in Lua such as 

         

        myNewTimer = createTimer( period, luaFunctionCallback)

         

        So I was passed the callback function into the C code which
        meant I could stash it away in the registry and then when the
        timer elapsed on the C side, I did this

         

         // push function indexed by registryRef from the registry back
        onto the stack

        // the lua callback function in this case

        lua_rawgeti(local_L, LUA_REGISTRYINDEX, registryRef);

        /* the first function argument is the timerID */ 

        lua_pushinteger(local_L, myTimerID); 

        /* call the function with 1 argument, return 0 result */ 

        pcallResult = lua_pcall(local_L,1,0,0);    // pops 2 params from
        stack

        

        I am guessing the solution is somewhat similar but cant figure
        it out.

         

        Many thanks for any assistance

         

           Geoff

         

         

         

      
    
    You are right. The solution is similar.

    

    In your Timer example, you retrieved the callback Lua function which
    was passed to the C side

    when the timer was created. While in your button press example, you
    should retrieve the button

    object first, then call the specified method on that object.

    

    Thus said, I assumed you had used some method (e.g.
    GetWindowLongPtr) to associate the

    button object (from Lua) with the *native* button object of the
    underlying GUI system.

    

    Following is an imaginary example to illustrate this idea to bind
    Lua to Win32 message system.

    Just as an illustration, can't claim the correctness.

----------------------------------------------------------------------------------

    

    [inside the window procedure of the button]

    [...]

    

    case WM_LBUTTONUP:

        // first retrive the associated object of the Lua side.

        // the following is just an demo method using GWLP_USERDATA to
    store L

        // and the registry to store the Lua object (this time a Lua
    table).

        lua_State * L = GetWindowLongPtr(hwnd, GWLP_USERDATA);

        lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)hwnd);

    

        // then call the "action" method on the button object

        lua_getfield(L, -1, "action");

    

        // do some check

        if (!lua_isnil(L, -1)) {

            // push the self parameter

            lua_pushvalue(L, -2);

            lua_pcall(L, 1, 1, 0);

            // check the callback return value and do some processing

            [...]

        }

    case WM_XXXX:

    [...]

-------------------------------------------------------------------------------------

    
      
     		 	   		   		 	   		  

 		 	   		  
#include <stdlib.h>
#include <string.h>

#include <lua.h>
#include <lauxlib.h>




// keep this data private here
static lua_State *L_Store = 0;




#define GRAPHICS_BUTTON_TYPE    "GRAPHICS_BUTTON_TYPE"        // userdata type name



typedef struct userdataButton
{
  OSLIB_OBJECT *pToGraphicsObj;
} USERDATA_BUTTON;




///=================================================================================================
/// <summary> Button event to lua.  </summary>
///
/// <remarks> ..,  How the heck do I implement this to call the Lua defined action method ? </remarks>
///
/// <param name="pToObject"> [in,out] If non-null, to object. </param>
///=================================================================================================

// Cant figure out how to call from C the Lua method for the btn1 instance, eg btn1:action() 

void buttonEventToLua( OSLIB_OBJECT *pToObject)
{
   lua_State *local_L = L_Store;

   printf("local_L = 0x%0x\n", local_L);
   printf("pObj = 0x%0x\n", pToObject);

   stackdump(local_L);
  
   // dont know how to get the address of lua's btn1:action() method ? :(

}


///=================================================================================================
/// <summary> return ptr to a userdata. passed in the Index of the userdata on the stack. </summary>
///
/// <remarks> .., 03/11/2013. </remarks>
///
/// <param name="L">     [in] Lua state. </param>
/// <param name="index"> index of userdata </param>
///
/// <returns> null if it fails, else ptr to userdata object. </returns>
///=================================================================================================
static USERDATA_BUTTON *toUserdataButton (lua_State *L, int index)
{
  USERDATA_BUTTON *pUserdataButton = (USERDATA_BUTTON *)lua_touserdata(L, index);

  if (pUserdataButton == NULL)
     luaL_typerror(L, index, GRAPHICS_BUTTON_TYPE);

  return pUserdataButton;
}


///=================================================================================================
/// <summary> Pushs a userdata Button. </summary>
///
/// <remarks> .., 10/11/2013. </remarks>
///
/// <param name="L"> [in,out] If non-null, the. </param>
///
/// <returns> null if it fails, else. </returns>
///=================================================================================================

static USERDATA_BUTTON *pushUserdataButton (lua_State *L)
{
   USERDATA_BUTTON *pUserdata = (USERDATA_BUTTON *)lua_newuserdata(L, sizeof(USERDATA_BUTTON));

   // be tidy and clear it
   memset(pUserdata,0, sizeof(USERDATA_BUTTON));

   //stackdump(L);
   luaL_getmetatable(L, GRAPHICS_BUTTON_TYPE);   // get Button's metatable
   lua_setmetatable(L, -2);                     // Set this metatable onto or new objects userdata
   return pUserdata;
}



///=================================================================================================
/// <summary> Check userdata Button. </summary>
///
/// <remarks> .., 10/11/2013. Errors ot if wrong userdata type </remarks>
///
/// <param name="L">     [in,out] If non-null, the. </param>
/// <param name="index"> Zero-based index of the. </param>
///
/// <returns> null if it fails, else. </returns>
///=================================================================================================

static USERDATA_BUTTON *checkUserdataButton (lua_State *L, int index)
{
USERDATA_BUTTON *pUserdataButton;

  luaL_checktype(L, index, LUA_TUSERDATA);

  pUserdataButton = (USERDATA_BUTTON *)luaL_checkudata(L, index, GRAPHICS_BUTTON_TYPE);
  if (pUserdataButton == NULL)
     luaL_typerror(L, index, GRAPHICS_BUTTON_TYPE);

  return pUserdataButton;
}

//--------------------------------------------------------------------------



///=================================================================================================
/// <summary> Ud garbage collect. </summary>
///
/// <remarks> .., 12/11/2013. </remarks>
///
/// <param name="L"> [in,out] If non-null, the. </param>
///
/// <returns> . </returns>
///=================================================================================================

static int udGarbageCollect (lua_State *L)
{
  USERDATA_BUTTON *pToUserData;
  
  //stackdump(L);

  // userdata is on the Lua stack at index 1
  pToUserData = toUserdataButton(L, 1);

  printf("bye, bye, USERDATA_BUTTON = %p\n", pToUserData);

  // ptr to Graphics object could be null if it has already been retired
  if (pToUserData->pToGraphicsObj)
     destroyObjectByPtr(pToUserData->pToGraphicsObj);

  return 0;
}


static int toStringForButton(lua_State *L)
{
  // Return some info about the button
  // This is easy just snipped it out for now
}




///=================================================================================================
/// <summary> Ud new button. </summary>
///
/// <remarks> .., 12/11/2013. </remarks>
///
/// <param name="L"> [in,out] If non-null, the. </param>
///
/// <returns> . </returns>
///=================================================================================================

static int udNewButton (lua_State *L)
{
   int x, y, width, ht, radius, nargs;
   const char *buttonText;
   const char *objectName;
   USERDATA_BUTTON *pToUserdata;
   OSLIB_OBJECT *objPtr;

   //stackdump(L);
   nargs = lua_gettop(L);

   if (nargs == 7)   // expecting 7 args  objectName,x,y,width,height,radius, ButtonText
   {
      // get the constructor data off the stack
      objectName = luaL_checkstring(L, 1);
      x = luaL_checkint(L, 2);
      y = luaL_checkint(L, 3);
      width = luaL_checkint(L, 4);
      ht = luaL_checkint(L, 5);
      radius = luaL_checkint(L, 6);
      buttonText = luaL_checkstring(L, 7);

      pToUserdata = pushUserdataButton(L);     // create new userdata and push it onto stack, also sets its metatable

      objPtr = createObject(LIB_GRAPHICS_BUTTON, (char*)objectName);  // get a ptr to an unused button from the retired list and name this button
      if (objPtr)
      {
         pToUserdata->pToGraphicsObj = objPtr;      // pointer to Graphics Button object in the userdata instance
         
         OSLIBx_SetTextUtf8(objPtr, (char*)buttonText);    // set the text that appears on the button

         objPtr->position.x = x;
         objPtr->position.y = y;
         objPtr->size.cx = width;
         objPtr->size.cy = ht;
         objPtr->radius = radius;
      }
   }
   else
   {
      luaL_error(L,"newButton() function must take 7 arguments\r\n");
   }
   return 1;
}



static int udShow(lua_State *L)
{
   USERDATA_BUTTON *pToUserdata;

   pToUserdata = checkUserdataButton(L, 1);   // userdata is on the Lua stack at index 1

   // ptr to Graphics object could be null if it has already been retired
   if (pToUserdata->pToGraphicsObj)
   {
      OSLIB_InvalidateObject(pToUserdata->pToGraphicsObj, 1);
      OSLIB_ShowObject(pToUserdata->pToGraphicsObj, 1);
   }
   return 0;    // not returning any params
}



static const luaL_reg MetaMap[] =
{
   //  My Methods on this userdata
   {"show", udShow},

   // Lua Metamethods on this userdata   
   {"__tostring", toStringForButton},
   {"__gc", udGarbageCollect},
   {NULL, NULL}
};  // Metamap

static const luaL_reg Map[] = 
{
   {"newButton", udNewButton},
   {NULL, NULL}
}; // Function Map


///=================================================================================================
/// <summary> Luaopen ud example. </summary>
///
/// <remarks> .., 11/11/2013. </remarks>
///
/// <param name="L"> [in,out] If non-null, the. </param>
///
/// <returns> . </returns>
///=================================================================================================

int luaopen_ud_button(lua_State *L)
{ 
   //stackdump(L);

   // Stk: metaTable 
   luaL_newmetatable(L, GRAPHICS_BUTTON_TYPE);

  // Stk: metaTable | metaTable
   lua_pushvalue(L, -1);    //Push copy of metatable

   // Stk: metaTable 
   // #..  t[k] = v, where t is the value at -2 and v is the value at the top of the stack.
   // I.e  meta[__index] = meta  sorta self referential
   lua_setfield(L, -2, "__index");

   // Stk: metaTable 
   luaL_register(L, NULL, MetaMap);

    // Stk: metaTable | table of Map functions
   luaL_register(L, "graphics", Map);      // this sets the namespace registered

   return 1;
}


///=================================================================================================
/// <summary> This is the library fun. </summary>
///
/// <remarks> .., 12/11/2013. </remarks>
///
/// <param name="L"> [in] Lua state. </param>
///
/// <returns> . </returns>
///=================================================================================================

LUALIB_API int luaopen_userdataBookButton(lua_State *L)
{
   luaopen_ud_button (L);

   lua_pop(L, 2); // two tables left on stack so balance it up before returning

   L_Store = L;       // stash this in a static for use in passing event

   return 0;
}