]> git.sesse.net Git - vlc/blob - modules/misc/lua/callbacks.c
10c273246b6e19dea31bdfb16860c03360f78b50
[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 #include <vlc/vlc.h>
32
33 #include <lua.h>        /* Low level lua C API */
34 #include <lauxlib.h>    /* Higher level C API */
35 #include <lualib.h>     /* Lua libs */
36
37 #include "vlc.h"
38
39 typedef struct
40 {
41     int i_index;
42     int i_type;
43     lua_State *L;
44 }   vlclua_callback_t;
45
46 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
47                             vlc_value_t oldval, vlc_value_t newval,
48                             void *p_data )
49 {
50     vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
51     lua_State *L = p_callback->L;
52
53     /* <empty stack> */
54     lua_getglobal( L, "vlc" );
55     /* vlc */
56     lua_getfield( L, -1, "callbacks" );
57     /* vlc callbacks */
58     lua_remove( L, -2 );
59     /* callbacks */
60     lua_pushinteger( L, p_callback->i_index );
61     /* callbacks index */
62     lua_gettable( L, -2 );
63     /* callbacks callbacks[index] */
64     lua_remove( L, -2 );
65     /* callbacks[index] */
66     lua_getfield( L, -1, "callback" );
67     /* callbacks[index] callback */
68     lua_pushstring( L, psz_var );
69     /* callbacks[index] callback var */
70     vlclua_pushvalue( L, p_callback->i_type, oldval );
71     /* callbacks[index] callback var oldval */
72     vlclua_pushvalue( L, p_callback->i_type, newval );
73     /* callbacks[index] callback var oldval newval */
74     lua_getfield( L, -5, "data" );
75     /* callbacks[index] callback var oldval newval data */
76     lua_remove( L, -6 );
77     /* callback var oldval newval data */
78
79     if( lua_pcall( L, 4, 0, 0 ) )
80     {
81         /* errormessage */
82         const char *psz_err = lua_tostring( L, -1 );
83         msg_Err( p_this, "Error while runing lua interface callback: %s",
84                  psz_err );
85         /* empty the stack (should only contain the error message) */
86         lua_settop( L, 0 );
87         return VLC_EGENERIC;
88     }
89
90     /* empty the stack (should already be empty) */
91     lua_settop( L, 0 );
92
93     return VLC_SUCCESS;
94 }
95
96 int vlclua_add_callback( lua_State *L )
97 {
98     vlclua_callback_t *p_callback;
99     static int i_index = 0;
100     vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
101     const char *psz_var = luaL_checkstring( L, 2 );
102     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
103     if( !lua_isfunction( L, 3 ) )
104         return vlclua_error( L );
105     i_index++;
106
107     p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
108     if( !p_callback )
109         return vlclua_error( L );
110
111     /* obj var func data */
112     lua_getglobal( L, "vlc" );
113     /* obj var func data vlc */
114     lua_getfield( L, -1, "callbacks" );
115     if( lua_isnil( L, -1 ) )
116     {
117         lua_pop( L, 1 );
118         lua_newtable( L );
119         lua_setfield( L, -2, "callbacks" );
120         lua_getfield( L, -1, "callbacks" );
121     }
122     /* obj var func data vlc callbacks */
123     lua_remove( L, -2 );
124     /* obj var func data callbacks */
125     lua_pushinteger( L, i_index );
126     /* obj var func data callbacks index */
127     lua_insert( L, -4 );
128     /* obj var index func data callbacks */
129     lua_insert( L, -4 );
130     /* obj var callbacks index func data */
131     lua_createtable( L, 0, 0 );
132     /* obj var callbacks index func data cbtable */
133     lua_insert( L, -2 );
134     /* obj var callbacks index func cbtable data */
135     lua_setfield( L, -2, "data" );
136     /* obj var callbacks index func cbtable */
137     lua_insert( L, -2 );
138     /* obj var callbacks index cbtable func */
139     lua_setfield( L, -2, "callback" );
140     /* obj var callbacks index cbtable */
141     lua_pushlightuserdata( L, p_obj ); /* will be needed in vlclua_del_callback */
142     /* obj var callbacks index cbtable p_obj */
143     lua_setfield( L, -2, "private1" );
144     /* obj var callbacks index cbtable */
145     lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
146     /* obj var callbacks index cbtable var */
147     lua_setfield( L, -2, "private2" );
148     /* obj var callbacks index cbtable */
149     lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
150     /* obj var callbacks index cbtable p_callback */
151     lua_setfield( L, -2, "private3" );
152     /* obj var callbacks index cbtable */
153     lua_settable( L, -3 );
154     /* obj var callbacks */
155     lua_pop( L, 3 );
156     /* <empty stack> */
157
158     /* Do not move this before the lua specific code (it somehow changes
159      * the function in the stack to nil) */
160     p_callback->i_index = i_index;
161     p_callback->i_type = var_Type( p_obj, psz_var );
162     p_callback->L = lua_newthread( L );
163
164     var_AddCallback( p_obj, psz_var, vlclua_callback, p_callback );
165     return 0;
166 }
167
168 int vlclua_del_callback( lua_State *L )
169 {
170     vlclua_callback_t *p_callback;
171     vlc_bool_t b_found = VLC_FALSE;
172     vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
173     const char *psz_var = luaL_checkstring( L, 2 );
174     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
175     if( !lua_isfunction( L, 3 ) )
176         return vlclua_error( L );
177
178     /* obj var func data */
179     lua_getglobal( L, "vlc" );
180     /* obj var func data vlc */
181     lua_getfield( L, -1, "callbacks" );
182     if( lua_isnil( L, -1 ) )
183         return luaL_error( L, "Couldn't find matching callback." );
184     /* obj var func data vlc callbacks */
185     lua_remove( L, -2 );
186     /* obj var func data callbacks */
187     lua_pushnil( L );
188     /* obj var func data callbacks index */
189     while( lua_next( L, -2 ) )
190     {
191         /* obj var func data callbacks index value */
192         if( lua_isnumber( L, -2 ) )
193         {
194             lua_getfield( L, -1, "private2" );
195             /* obj var func data callbacks index value private2 */
196             if( lua_equal( L, 2, -1 ) ) /* var name is equal */
197             {
198                 lua_pop( L, 1 );
199                 /* obj var func data callbacks index value */
200                 lua_getfield( L, -1, "callback" );
201                 /* obj var func data callbacks index value callback */
202                 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
203                 {
204                     lua_pop( L, 1 );
205                     /* obj var func data callbacks index value */
206                     lua_getfield( L, -1, "data" ); /* callback data is equal */
207                     /* obj var func data callbacks index value data */
208                     if( lua_equal( L, 4, -1 ) )
209                     {
210                         vlc_object_t *p_obj2;
211                         lua_pop( L, 1 );
212                         /* obj var func data callbacks index value */
213                         lua_getfield( L, -1, "private1" );
214                         /* obj var func data callbacks index value private1 */
215                         p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
216                         if( p_obj2 == p_obj ) /* object is equal */
217                         {
218                             lua_pop( L, 1 );
219                             /* obj var func data callbacks index value */
220                             lua_getfield( L, -1, "private3" );
221                             /* obj var func data callbacks index value private3 */
222                             p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
223                             lua_pop( L, 2 );
224                             /* obj var func data callbacks index */
225                             b_found = VLC_TRUE;
226                             break;
227                         }
228                         else
229                         {
230                             /* obj var func data callbacks index value private1 */
231                             lua_pop( L, 1 );
232                             /* obj var func data callbacks index value */
233                         }
234                     }
235                     else
236                     {
237                         /* obj var func data callbacks index value data */
238                         lua_pop( L, 1 );
239                         /* obj var func data callbacks index value */
240                     }
241                 }
242                 else
243                 {
244                     /* obj var func data callbacks index value callback */
245                     lua_pop( L, 1 );
246                     /* obj var func data callbacks index value */
247                 }
248             }
249             else
250             {
251                 /* obj var func data callbacks index value private2 */
252                 lua_pop( L, 1 );
253                 /* obj var func data callbacks index value */
254             }
255         }
256         /* obj var func data callbacks index value */
257         lua_pop( L, 1 );
258         /* obj var func data callbacks index */
259     }
260     if( b_found == VLC_FALSE )
261         /* obj var func data callbacks */
262         return luaL_error( L, "Couldn't find matching callback." );
263     /* else */
264         /* obj var func data callbacks index*/
265
266     var_DelCallback( p_obj, psz_var, vlclua_callback, p_callback );
267     free( p_callback );
268
269     /* obj var func data callbacks index */
270     lua_pushnil( L );
271     /* obj var func data callbacks index nil */
272     lua_settable( L, -3 ); /* delete the callback table entry */
273     /* obj var func data callbacks */
274     lua_pop( L, 5 );
275     /* <empty stack> */
276     return 0;
277 }