]> git.sesse.net Git - vlc/blob - modules/misc/lua/libs/variables.c
playlist: Make sure we don't pl_Release(p_playlist).
[vlc] / modules / misc / lua / libs / variables.c
1 /*****************************************************************************
2  * variables.c: Generic lua<->vlc variables interface
3  *****************************************************************************
4  * Copyright (C) 2007-2008 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_common.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 #include "../libs.h"
43 #include "variables.h"
44 #include "objects.h"
45
46 /*****************************************************************************
47  * Variables handling
48  *****************************************************************************/
49 int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val )
50 {
51     switch( i_type &= 0xf0 )
52     {
53         case VLC_VAR_VOID:
54             vlclua_error( L );
55             break;
56         case VLC_VAR_BOOL:
57             lua_pushboolean( L, val.b_bool );
58             break;
59         case VLC_VAR_INTEGER:
60             lua_pushinteger( L, val.i_int );
61             break;
62         case VLC_VAR_STRING:
63             lua_pushstring( L, val.psz_string );
64             break;
65         case VLC_VAR_FLOAT:
66             lua_pushnumber( L, val.f_float );
67             break;
68         case VLC_VAR_TIME:
69             /* FIXME? (we're losing some precision, but does it really matter?) */
70             lua_pushnumber( L, ((double)val.i_time)/1000000. );
71             break;
72         case VLC_VAR_ADDRESS:
73             vlclua_error( L );
74             break;
75         case VLC_VAR_MUTEX:
76             vlclua_error( L );
77             break;
78         case VLC_VAR_LIST:
79             {
80                 int i_count = val.p_list->i_count;
81                 int i;
82                 lua_createtable( L, i_count, 0 );
83                 for( i = 0; i < i_count; i++ )
84                 {
85                     lua_pushinteger( L, i+1 );
86                     if( !vlclua_pushvalue( L, val.p_list->pi_types[i],
87                                            val.p_list->p_values[i] ) )
88                         lua_pushnil( L );
89                     lua_settable( L, -3 );
90                 }
91             }
92             break;
93         default:
94             vlclua_error( L );
95     }
96     return 1;
97 }
98
99 static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
100 {
101     switch( i_type & 0xf0 )
102     {
103         case VLC_VAR_VOID:
104             break;
105         case VLC_VAR_BOOL:
106             val->b_bool = luaL_checkboolean( L, -1 );
107             break;
108         case VLC_VAR_INTEGER:
109             val->i_int = luaL_checkint( L, -1 );
110             break;
111         case VLC_VAR_STRING:
112             val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
113             break;
114         case VLC_VAR_FLOAT:
115             val->f_float = luaL_checknumber( L, -1 );
116             break;
117         case VLC_VAR_TIME:
118             {
119                 double f = luaL_checknumber( L, -1 );
120                 val->i_time = (int64_t)(f*1000000.);
121             }
122             break;
123         case VLC_VAR_ADDRESS:
124             vlclua_error( L );
125             break;
126         case VLC_VAR_MUTEX:
127             vlclua_error( L );
128             break;
129         case VLC_VAR_LIST:
130             vlclua_error( L );
131             break;
132         default:
133             vlclua_error( L );
134     }
135     return 1;
136 }
137
138 static int vlclua_var_get( lua_State *L )
139 {
140     int i_type;
141     vlc_value_t val;
142     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
143     const char *psz_var = luaL_checkstring( L, 2 );
144     i_type = var_Type( *pp_obj, psz_var );
145     var_Get( *pp_obj, psz_var, &val );
146     lua_pop( L, 2 );
147     return vlclua_pushvalue( L, i_type, val );
148 }
149
150 static int vlclua_var_set( lua_State *L )
151 {
152     int i_type;
153     vlc_value_t val;
154     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
155     const char *psz_var = luaL_checkstring( L, 2 );
156     int i_ret;
157     i_type = var_Type( *pp_obj, psz_var );
158     vlclua_tovalue( L, i_type, &val );
159     i_ret = var_Set( *pp_obj, psz_var, val );
160     lua_pop( L, 3 );
161     return vlclua_push_ret( L, i_ret );
162 }
163
164 static int vlclua_var_get_list( lua_State *L )
165 {
166     vlc_value_t val;
167     vlc_value_t text;
168     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
169     const char *psz_var = luaL_checkstring( L, 2 );
170     int i_ret = var_Change( *pp_obj, psz_var, VLC_VAR_GETLIST, &val, &text );
171     if( i_ret < 0 ) return vlclua_push_ret( L, i_ret );
172     vlclua_pushvalue( L, VLC_VAR_LIST, val );
173     vlclua_pushvalue( L, VLC_VAR_LIST, text );
174     var_Change( *pp_obj, psz_var, VLC_VAR_FREELIST, &val, &text );
175     return 2;
176 }
177
178 static int vlclua_command( lua_State *L )
179 {
180     vlc_object_t * p_this = vlclua_get_this( L );
181     const char *psz_name;
182     const char *psz_cmd;
183     const char *psz_arg;
184     char *psz_msg;
185     psz_name = luaL_checkstring( L, 1 );
186     psz_cmd = luaL_checkstring( L, 2 );
187     psz_arg = luaL_checkstring( L, 3 );
188     lua_pop( L, 3 );
189     var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
190     if( psz_msg )
191     {
192         lua_pushstring( L, psz_msg );
193         free( psz_msg );
194     }
195     else
196     {
197         lua_pushstring( L, "" );
198     }
199     return 1;
200 }
201
202 static int vlclua_libvlc_command( lua_State *L )
203 {
204     vlc_object_t * p_this = vlclua_get_this( L );
205     const char *psz_cmd;
206     vlc_value_t val_arg;
207     psz_cmd = luaL_checkstring( L, 1 );
208     val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
209     lua_pop( L, 2 );
210     if( !var_Type( p_this->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
211     {
212         free( val_arg.psz_string );
213         return luaL_error( L, "libvlc's \"%s\" is not a command",
214                            psz_cmd );
215     }
216
217     return vlclua_push_ret( L,
218                             var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
219 }
220
221 int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
222                                 const char *psz_name )
223 {
224     bool b_bool;
225     if( lua_gettop( L ) > 1 ) return vlclua_error( L );
226
227     if( lua_gettop( L ) == 0 )
228         b_bool = !var_GetBool( p_obj, psz_name );
229     else /* lua_gettop( L ) == 1 */
230     {
231         b_bool = luaL_checkboolean( L, -1 )?true:false;
232         lua_pop( L, 1 );
233     }
234
235     if( b_bool != var_GetBool( p_obj, psz_name ) )
236         var_SetBool( p_obj, psz_name, b_bool );
237
238     lua_pushboolean( L, b_bool );
239     return 1;
240 }
241
242 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
243 {
244     luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
245     return lua_topointer( L, narg );
246 }
247
248 typedef struct
249 {
250     int i_index;
251     int i_type;
252     lua_State *L;
253 }   vlclua_callback_t;
254
255 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
256                             vlc_value_t oldval, vlc_value_t newval,
257                             void *p_data )
258 {
259     vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
260     lua_State *L = p_callback->L;
261
262     /* <empty stack> */
263     lua_getglobal( L, "vlc" );
264     /* vlc */
265     lua_getfield( L, -1, "callbacks" );
266     /* vlc callbacks */
267     lua_remove( L, -2 );
268     /* callbacks */
269     lua_pushinteger( L, p_callback->i_index );
270     /* callbacks index */
271     lua_gettable( L, -2 );
272     /* callbacks callbacks[index] */
273     lua_remove( L, -2 );
274     /* callbacks[index] */
275     lua_getfield( L, -1, "callback" );
276     /* callbacks[index] callback */
277     lua_pushstring( L, psz_var );
278     /* callbacks[index] callback var */
279     vlclua_pushvalue( L, p_callback->i_type, oldval );
280     /* callbacks[index] callback var oldval */
281     vlclua_pushvalue( L, p_callback->i_type, newval );
282     /* callbacks[index] callback var oldval newval */
283     lua_getfield( L, -5, "data" );
284     /* callbacks[index] callback var oldval newval data */
285     lua_remove( L, -6 );
286     /* callback var oldval newval data */
287
288     if( lua_pcall( L, 4, 0, 0 ) )
289     {
290         /* errormessage */
291         const char *psz_err = lua_tostring( L, -1 );
292         msg_Err( p_this, "Error while runing lua interface callback: %s",
293                  psz_err );
294         /* empty the stack (should only contain the error message) */
295         lua_settop( L, 0 );
296         return VLC_EGENERIC;
297     }
298
299     /* empty the stack (should already be empty) */
300     lua_settop( L, 0 );
301
302     return VLC_SUCCESS;
303 }
304
305 static int vlclua_add_callback( lua_State *L )
306 {
307     vlclua_callback_t *p_callback;
308     static int i_index = 0;
309     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
310     const char *psz_var = luaL_checkstring( L, 2 );
311     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
312     if( !lua_isfunction( L, 3 ) )
313         return vlclua_error( L );
314     i_index++;
315
316     p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
317     if( !p_callback )
318         return vlclua_error( L );
319
320     /* obj var func data */
321     lua_getglobal( L, "vlc" );
322     /* obj var func data vlc */
323     lua_getfield( L, -1, "callbacks" );
324     if( lua_isnil( L, -1 ) )
325     {
326         lua_pop( L, 1 );
327         lua_newtable( L );
328         lua_setfield( L, -2, "callbacks" );
329         lua_getfield( L, -1, "callbacks" );
330     }
331     /* obj var func data vlc callbacks */
332     lua_remove( L, -2 );
333     /* obj var func data callbacks */
334     lua_pushinteger( L, i_index );
335     /* obj var func data callbacks index */
336     lua_insert( L, -4 );
337     /* obj var index func data callbacks */
338     lua_insert( L, -4 );
339     /* obj var callbacks index func data */
340     lua_createtable( L, 0, 0 );
341     /* obj var callbacks index func data cbtable */
342     lua_insert( L, -2 );
343     /* obj var callbacks index func cbtable data */
344     lua_setfield( L, -2, "data" );
345     /* obj var callbacks index func cbtable */
346     lua_insert( L, -2 );
347     /* obj var callbacks index cbtable func */
348     lua_setfield( L, -2, "callback" );
349     /* obj var callbacks index cbtable */
350     lua_pushlightuserdata( L, *pp_obj ); /* will be needed in vlclua_del_callback */
351     /* obj var callbacks index cbtable p_obj */
352     lua_setfield( L, -2, "private1" );
353     /* obj var callbacks index cbtable */
354     lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
355     /* obj var callbacks index cbtable var */
356     lua_setfield( L, -2, "private2" );
357     /* obj var callbacks index cbtable */
358     lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
359     /* obj var callbacks index cbtable p_callback */
360     lua_setfield( L, -2, "private3" );
361     /* obj var callbacks index cbtable */
362     lua_settable( L, -3 );
363     /* obj var callbacks */
364     lua_pop( L, 3 );
365     /* <empty stack> */
366
367     /* Do not move this before the lua specific code (it somehow changes
368      * the function in the stack to nil) */
369     p_callback->i_index = i_index;
370     p_callback->i_type = var_Type( *pp_obj, psz_var );
371     p_callback->L = lua_newthread( L ); /* Do we have to keep a reference to this thread somewhere to prevent garbage collection? */
372
373     var_AddCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
374     return 0;
375 }
376
377 static int vlclua_del_callback( lua_State *L )
378 {
379     vlclua_callback_t *p_callback;
380     bool b_found = false;
381     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
382     const char *psz_var = luaL_checkstring( L, 2 );
383     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
384     if( !lua_isfunction( L, 3 ) )
385         return vlclua_error( L );
386
387     /* obj var func data */
388     lua_getglobal( L, "vlc" );
389     /* obj var func data vlc */
390     lua_getfield( L, -1, "callbacks" );
391     if( lua_isnil( L, -1 ) )
392         return luaL_error( L, "Couldn't find matching callback." );
393     /* obj var func data vlc callbacks */
394     lua_remove( L, -2 );
395     /* obj var func data callbacks */
396     lua_pushnil( L );
397     /* obj var func data callbacks index */
398     while( lua_next( L, -2 ) )
399     {
400         /* obj var func data callbacks index value */
401         if( lua_isnumber( L, -2 ) )
402         {
403             lua_getfield( L, -1, "private2" );
404             /* obj var func data callbacks index value private2 */
405             if( lua_equal( L, 2, -1 ) ) /* var name is equal */
406             {
407                 lua_pop( L, 1 );
408                 /* obj var func data callbacks index value */
409                 lua_getfield( L, -1, "callback" );
410                 /* obj var func data callbacks index value callback */
411                 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
412                 {
413                     lua_pop( L, 1 );
414                     /* obj var func data callbacks index value */
415                     lua_getfield( L, -1, "data" ); /* callback data is equal */
416                     /* obj var func data callbacks index value data */
417                     if( lua_equal( L, 4, -1 ) )
418                     {
419                         vlc_object_t *p_obj2;
420                         lua_pop( L, 1 );
421                         /* obj var func data callbacks index value */
422                         lua_getfield( L, -1, "private1" );
423                         /* obj var func data callbacks index value private1 */
424                         p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
425                         if( p_obj2 == *pp_obj ) /* object is equal */
426                         {
427                             lua_pop( L, 1 );
428                             /* obj var func data callbacks index value */
429                             lua_getfield( L, -1, "private3" );
430                             /* obj var func data callbacks index value private3 */
431                             p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
432                             lua_pop( L, 2 );
433                             /* obj var func data callbacks index */
434                             b_found = true;
435                             break;
436                         }
437                         else
438                         {
439                             /* obj var func data callbacks index value private1 */
440                             lua_pop( L, 1 );
441                             /* obj var func data callbacks index value */
442                         }
443                     }
444                     else
445                     {
446                         /* obj var func data callbacks index value data */
447                         lua_pop( L, 1 );
448                         /* obj var func data callbacks index value */
449                     }
450                 }
451                 else
452                 {
453                     /* obj var func data callbacks index value callback */
454                     lua_pop( L, 1 );
455                     /* obj var func data callbacks index value */
456                 }
457             }
458             else
459             {
460                 /* obj var func data callbacks index value private2 */
461                 lua_pop( L, 1 );
462                 /* obj var func data callbacks index value */
463             }
464         }
465         /* obj var func data callbacks index value */
466         lua_pop( L, 1 );
467         /* obj var func data callbacks index */
468     }
469     if( b_found == false )
470         /* obj var func data callbacks */
471         return luaL_error( L, "Couldn't find matching callback." );
472     /* else */
473         /* obj var func data callbacks index*/
474
475     var_DelCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
476     free( p_callback );
477
478     /* obj var func data callbacks index */
479     lua_pushnil( L );
480     /* obj var func data callbacks index nil */
481     lua_settable( L, -3 ); /* delete the callback table entry */
482     /* obj var func data callbacks */
483     lua_pop( L, 5 );
484     /* <empty stack> */
485     return 0;
486 }
487
488 /*****************************************************************************
489  *
490  *****************************************************************************/
491 static const luaL_Reg vlclua_var_reg[] = {
492     { "get", vlclua_var_get },
493     { "get_list", vlclua_var_get_list },
494     { "set", vlclua_var_set },
495     { "add_callback", vlclua_add_callback },
496     { "del_callback", vlclua_del_callback },
497     { "command", vlclua_command },
498     { "libvlc_command", vlclua_libvlc_command },
499     { NULL, NULL }
500 };
501
502 void luaopen_variables( lua_State *L )
503 {
504     lua_newtable( L );
505     luaL_register( L, NULL, vlclua_var_reg );
506     lua_setfield( L, -2, "var" );
507 }