]> git.sesse.net Git - vlc/blob - modules/misc/lua/callbacks.c
update module LIST file.
[vlc] / modules / misc / lua / callbacks.c
1 /*****************************************************************************
2  * callbacks.c: Generic lua<->vlc callbacks interface
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifndef  _GNU_SOURCE
28 #   define  _GNU_SOURCE
29 #endif
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <vlc/vlc.h>
36
37 #include <lua.h>        /* Low level lua C API */
38 #include <lauxlib.h>    /* Higher level C API */
39 #include <lualib.h>     /* Lua libs */
40
41 #include "vlc.h"
42
43 typedef struct
44 {
45     int i_index;
46     int i_type;
47     lua_State *L;
48 }   vlclua_callback_t;
49
50 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
51                             vlc_value_t oldval, vlc_value_t newval,
52                             void *p_data )
53 {
54     vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
55     lua_State *L = p_callback->L;
56
57     /* <empty stack> */
58     lua_getglobal( L, "vlc" );
59     /* vlc */
60     lua_getfield( L, -1, "callbacks" );
61     /* vlc callbacks */
62     lua_remove( L, -2 );
63     /* callbacks */
64     lua_pushinteger( L, p_callback->i_index );
65     /* callbacks index */
66     lua_gettable( L, -2 );
67     /* callbacks callbacks[index] */
68     lua_remove( L, -2 );
69     /* callbacks[index] */
70     lua_getfield( L, -1, "callback" );
71     /* callbacks[index] callback */
72     lua_pushstring( L, psz_var );
73     /* callbacks[index] callback var */
74     vlclua_pushvalue( L, p_callback->i_type, oldval );
75     /* callbacks[index] callback var oldval */
76     vlclua_pushvalue( L, p_callback->i_type, newval );
77     /* callbacks[index] callback var oldval newval */
78     lua_getfield( L, -5, "data" );
79     /* callbacks[index] callback var oldval newval data */
80     lua_remove( L, -6 );
81     /* callback var oldval newval data */
82
83     if( lua_pcall( L, 4, 0, 0 ) )
84     {
85         /* errormessage */
86         const char *psz_err = lua_tostring( L, -1 );
87         msg_Err( p_this, "Error while runing lua interface callback: %s",
88                  psz_err );
89         /* empty the stack (should only contain the error message) */
90         lua_settop( L, 0 );
91         return VLC_EGENERIC;
92     }
93
94     /* empty the stack (should already be empty) */
95     lua_settop( L, 0 );
96
97     return VLC_SUCCESS;
98 }
99
100 int vlclua_add_callback( lua_State *L )
101 {
102     vlclua_callback_t *p_callback;
103     static int i_index = 0;
104     vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
105     const char *psz_var = luaL_checkstring( L, 2 );
106     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
107     if( !lua_isfunction( L, 3 ) )
108         return vlclua_error( L );
109     i_index++;
110
111     p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
112     if( !p_callback )
113         return vlclua_error( L );
114
115     /* obj var func data */
116     lua_getglobal( L, "vlc" );
117     /* obj var func data vlc */
118     lua_getfield( L, -1, "callbacks" );
119     if( lua_isnil( L, -1 ) )
120     {
121         lua_pop( L, 1 );
122         lua_newtable( L );
123         lua_setfield( L, -2, "callbacks" );
124         lua_getfield( L, -1, "callbacks" );
125     }
126     /* obj var func data vlc callbacks */
127     lua_remove( L, -2 );
128     /* obj var func data callbacks */
129     lua_pushinteger( L, i_index );
130     /* obj var func data callbacks index */
131     lua_insert( L, -4 );
132     /* obj var index func data callbacks */
133     lua_insert( L, -4 );
134     /* obj var callbacks index func data */
135     lua_createtable( L, 0, 0 );
136     /* obj var callbacks index func data cbtable */
137     lua_insert( L, -2 );
138     /* obj var callbacks index func cbtable data */
139     lua_setfield( L, -2, "data" );
140     /* obj var callbacks index func cbtable */
141     lua_insert( L, -2 );
142     /* obj var callbacks index cbtable func */
143     lua_setfield( L, -2, "callback" );
144     /* obj var callbacks index cbtable */
145     lua_pushlightuserdata( L, p_obj ); /* will be needed in vlclua_del_callback */
146     /* obj var callbacks index cbtable p_obj */
147     lua_setfield( L, -2, "private1" );
148     /* obj var callbacks index cbtable */
149     lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
150     /* obj var callbacks index cbtable var */
151     lua_setfield( L, -2, "private2" );
152     /* obj var callbacks index cbtable */
153     lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
154     /* obj var callbacks index cbtable p_callback */
155     lua_setfield( L, -2, "private3" );
156     /* obj var callbacks index cbtable */
157     lua_settable( L, -3 );
158     /* obj var callbacks */
159     lua_pop( L, 3 );
160     /* <empty stack> */
161
162     /* Do not move this before the lua specific code (it somehow changes
163      * the function in the stack to nil) */
164     p_callback->i_index = i_index;
165     p_callback->i_type = var_Type( p_obj, psz_var );
166     p_callback->L = lua_newthread( L ); /* Do we have to keep a reference to this thread somewhere to prevent garbage collection? */
167
168     var_AddCallback( p_obj, psz_var, vlclua_callback, p_callback );
169     return 0;
170 }
171
172 int vlclua_del_callback( lua_State *L )
173 {
174     vlclua_callback_t *p_callback;
175     vlc_bool_t b_found = VLC_FALSE;
176     vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
177     const char *psz_var = luaL_checkstring( L, 2 );
178     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
179     if( !lua_isfunction( L, 3 ) )
180         return vlclua_error( L );
181
182     /* obj var func data */
183     lua_getglobal( L, "vlc" );
184     /* obj var func data vlc */
185     lua_getfield( L, -1, "callbacks" );
186     if( lua_isnil( L, -1 ) )
187         return luaL_error( L, "Couldn't find matching callback." );
188     /* obj var func data vlc callbacks */
189     lua_remove( L, -2 );
190     /* obj var func data callbacks */
191     lua_pushnil( L );
192     /* obj var func data callbacks index */
193     while( lua_next( L, -2 ) )
194     {
195         /* obj var func data callbacks index value */
196         if( lua_isnumber( L, -2 ) )
197         {
198             lua_getfield( L, -1, "private2" );
199             /* obj var func data callbacks index value private2 */
200             if( lua_equal( L, 2, -1 ) ) /* var name is equal */
201             {
202                 lua_pop( L, 1 );
203                 /* obj var func data callbacks index value */
204                 lua_getfield( L, -1, "callback" );
205                 /* obj var func data callbacks index value callback */
206                 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
207                 {
208                     lua_pop( L, 1 );
209                     /* obj var func data callbacks index value */
210                     lua_getfield( L, -1, "data" ); /* callback data is equal */
211                     /* obj var func data callbacks index value data */
212                     if( lua_equal( L, 4, -1 ) )
213                     {
214                         vlc_object_t *p_obj2;
215                         lua_pop( L, 1 );
216                         /* obj var func data callbacks index value */
217                         lua_getfield( L, -1, "private1" );
218                         /* obj var func data callbacks index value private1 */
219                         p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
220                         if( p_obj2 == p_obj ) /* object is equal */
221                         {
222                             lua_pop( L, 1 );
223                             /* obj var func data callbacks index value */
224                             lua_getfield( L, -1, "private3" );
225                             /* obj var func data callbacks index value private3 */
226                             p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
227                             lua_pop( L, 2 );
228                             /* obj var func data callbacks index */
229                             b_found = VLC_TRUE;
230                             break;
231                         }
232                         else
233                         {
234                             /* obj var func data callbacks index value private1 */
235                             lua_pop( L, 1 );
236                             /* obj var func data callbacks index value */
237                         }
238                     }
239                     else
240                     {
241                         /* obj var func data callbacks index value data */
242                         lua_pop( L, 1 );
243                         /* obj var func data callbacks index value */
244                     }
245                 }
246                 else
247                 {
248                     /* obj var func data callbacks index value callback */
249                     lua_pop( L, 1 );
250                     /* obj var func data callbacks index value */
251                 }
252             }
253             else
254             {
255                 /* obj var func data callbacks index value private2 */
256                 lua_pop( L, 1 );
257                 /* obj var func data callbacks index value */
258             }
259         }
260         /* obj var func data callbacks index value */
261         lua_pop( L, 1 );
262         /* obj var func data callbacks index */
263     }
264     if( b_found == VLC_FALSE )
265         /* obj var func data callbacks */
266         return luaL_error( L, "Couldn't find matching callback." );
267     /* else */
268         /* obj var func data callbacks index*/
269
270     var_DelCallback( p_obj, psz_var, vlclua_callback, p_callback );
271     free( p_callback );
272
273     /* obj var func data callbacks index */
274     lua_pushnil( L );
275     /* obj var func data callbacks index nil */
276     lua_settable( L, -3 ); /* delete the callback table entry */
277     /* obj var func data callbacks */
278     lua_pop( L, 5 );
279     /* <empty stack> */
280     return 0;
281 }