]> git.sesse.net Git - vlc/blob - modules/misc/lua/libs/variables.c
Test pointers against NULL instead of 0.
[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     if( var_Get( *pp_obj, psz_var, &val ) != VLC_SUCCESS )
146         return 0;
147     lua_pop( L, 2 );
148     return vlclua_pushvalue( L, i_type, val );
149 }
150
151 static int vlclua_var_set( lua_State *L )
152 {
153     int i_type;
154     vlc_value_t val;
155     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
156     const char *psz_var = luaL_checkstring( L, 2 );
157     int i_ret;
158     i_type = var_Type( *pp_obj, psz_var );
159     vlclua_tovalue( L, i_type, &val );
160     i_ret = var_Set( *pp_obj, psz_var, val );
161     lua_pop( L, 3 );
162     return vlclua_push_ret( L, i_ret );
163 }
164
165 static int vlclua_var_create( lua_State *L )
166 {
167     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
168     const char *psz_var = luaL_checkstring( L, 2 );
169     int i_type;
170     switch( lua_type( L, 3 ) )
171     {
172         case LUA_TNUMBER:
173             i_type = VLC_VAR_FLOAT;
174             break;
175         case LUA_TBOOLEAN:
176             i_type = VLC_VAR_BOOL;
177             break;
178         case LUA_TSTRING:
179             i_type = VLC_VAR_STRING;
180             break;
181         default:
182             return 0;
183     }
184
185     int i_ret = var_Create( *pp_obj, psz_var, i_type );
186     if( i_ret != VLC_SUCCESS )
187         return vlclua_push_ret( L, i_ret );
188     vlc_value_t val;
189     vlclua_tovalue( L, i_type, &val );
190     return vlclua_push_ret( L, var_Set( *pp_obj, psz_var, val ) );
191 }
192
193 static int vlclua_var_get_list( lua_State *L )
194 {
195     vlc_value_t val;
196     vlc_value_t text;
197     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
198     const char *psz_var = luaL_checkstring( L, 2 );
199     int i_ret = var_Change( *pp_obj, psz_var, VLC_VAR_GETLIST, &val, &text );
200     if( i_ret < 0 ) return vlclua_push_ret( L, i_ret );
201     vlclua_pushvalue( L, VLC_VAR_LIST, val );
202     vlclua_pushvalue( L, VLC_VAR_LIST, text );
203     var_Change( *pp_obj, psz_var, VLC_VAR_FREELIST, &val, &text );
204     return 2;
205 }
206
207 static int vlclua_command( lua_State *L )
208 {
209     vlc_object_t * p_this = vlclua_get_this( L );
210     const char *psz_name;
211     const char *psz_cmd;
212     const char *psz_arg;
213     char *psz_msg;
214     psz_name = luaL_checkstring( L, 1 );
215     psz_cmd = luaL_checkstring( L, 2 );
216     psz_arg = luaL_checkstring( L, 3 );
217     lua_pop( L, 3 );
218     var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
219     if( psz_msg )
220     {
221         lua_pushstring( L, psz_msg );
222         free( psz_msg );
223     }
224     else
225     {
226         lua_pushstring( L, "" );
227     }
228     return 1;
229 }
230
231 static int vlclua_libvlc_command( lua_State *L )
232 {
233     vlc_object_t * p_this = vlclua_get_this( L );
234     const char *psz_cmd;
235     vlc_value_t val_arg;
236     psz_cmd = luaL_checkstring( L, 1 );
237     val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
238     lua_pop( L, 2 );
239     int i_type = var_Type( p_this->p_libvlc, psz_cmd );
240     if( ! i_type & VLC_VAR_ISCOMMAND )
241     {
242         free( val_arg.psz_string );
243         return luaL_error( L, "libvlc's \"%s\" is not a command",
244                            psz_cmd );
245     }
246
247     return vlclua_push_ret( L,
248                             var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
249 }
250
251 int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
252                                 const char *psz_name )
253 {
254     bool b_bool;
255     if( lua_gettop( L ) > 1 ) return vlclua_error( L );
256
257     if( lua_gettop( L ) == 0 )
258         b_bool = !var_GetBool( p_obj, psz_name );
259     else /* lua_gettop( L ) == 1 */
260     {
261         b_bool = luaL_checkboolean( L, -1 )?true:false;
262         lua_pop( L, 1 );
263     }
264
265     if( b_bool != var_GetBool( p_obj, psz_name ) )
266         var_SetBool( p_obj, psz_name, b_bool );
267
268     lua_pushboolean( L, b_bool );
269     return 1;
270 }
271
272 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
273 {
274     luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
275     return lua_topointer( L, narg );
276 }
277
278 typedef struct
279 {
280     int i_index;
281     int i_type;
282     lua_State *L;
283 }   vlclua_callback_t;
284
285 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
286                             vlc_value_t oldval, vlc_value_t newval,
287                             void *p_data )
288 {
289     vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
290     lua_State *L = p_callback->L;
291
292     /* <empty stack> */
293     lua_getglobal( L, "vlc" );
294     /* vlc */
295     lua_getfield( L, -1, "callbacks" );
296     /* vlc callbacks */
297     lua_remove( L, -2 );
298     /* callbacks */
299     lua_pushinteger( L, p_callback->i_index );
300     /* callbacks index */
301     lua_gettable( L, -2 );
302     /* callbacks callbacks[index] */
303     lua_remove( L, -2 );
304     /* callbacks[index] */
305     lua_getfield( L, -1, "callback" );
306     /* callbacks[index] callback */
307     lua_pushstring( L, psz_var );
308     /* callbacks[index] callback var */
309     vlclua_pushvalue( L, p_callback->i_type, oldval );
310     /* callbacks[index] callback var oldval */
311     vlclua_pushvalue( L, p_callback->i_type, newval );
312     /* callbacks[index] callback var oldval newval */
313     lua_getfield( L, -5, "data" );
314     /* callbacks[index] callback var oldval newval data */
315     lua_remove( L, -6 );
316     /* callback var oldval newval data */
317
318     if( lua_pcall( L, 4, 0, 0 ) )
319     {
320         /* errormessage */
321         const char *psz_err = lua_tostring( L, -1 );
322         msg_Err( p_this, "Error while runing lua interface callback: %s",
323                  psz_err );
324         /* empty the stack (should only contain the error message) */
325         lua_settop( L, 0 );
326         return VLC_EGENERIC;
327     }
328
329     /* empty the stack (should already be empty) */
330     lua_settop( L, 0 );
331
332     return VLC_SUCCESS;
333 }
334
335 static int vlclua_add_callback( lua_State *L )
336 {
337     vlclua_callback_t *p_callback;
338     static int i_index = 0;
339     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
340     const char *psz_var = luaL_checkstring( L, 2 );
341     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
342     if( !lua_isfunction( L, 3 ) )
343         return vlclua_error( L );
344     i_index++;
345
346     p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
347     if( !p_callback )
348         return vlclua_error( L );
349
350     /* obj var func data */
351     lua_getglobal( L, "vlc" );
352     /* obj var func data vlc */
353     lua_getfield( L, -1, "callbacks" );
354     if( lua_isnil( L, -1 ) )
355     {
356         lua_pop( L, 1 );
357         lua_newtable( L );
358         lua_setfield( L, -2, "callbacks" );
359         lua_getfield( L, -1, "callbacks" );
360     }
361     /* obj var func data vlc callbacks */
362     lua_remove( L, -2 );
363     /* obj var func data callbacks */
364     lua_pushinteger( L, i_index );
365     /* obj var func data callbacks index */
366     lua_insert( L, -4 );
367     /* obj var index func data callbacks */
368     lua_insert( L, -4 );
369     /* obj var callbacks index func data */
370     lua_createtable( L, 0, 0 );
371     /* obj var callbacks index func data cbtable */
372     lua_insert( L, -2 );
373     /* obj var callbacks index func cbtable data */
374     lua_setfield( L, -2, "data" );
375     /* obj var callbacks index func cbtable */
376     lua_insert( L, -2 );
377     /* obj var callbacks index cbtable func */
378     lua_setfield( L, -2, "callback" );
379     /* obj var callbacks index cbtable */
380     lua_pushlightuserdata( L, *pp_obj ); /* will be needed in vlclua_del_callback */
381     /* obj var callbacks index cbtable p_obj */
382     lua_setfield( L, -2, "private1" );
383     /* obj var callbacks index cbtable */
384     lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
385     /* obj var callbacks index cbtable var */
386     lua_setfield( L, -2, "private2" );
387     /* obj var callbacks index cbtable */
388     lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
389     /* obj var callbacks index cbtable p_callback */
390     lua_setfield( L, -2, "private3" );
391     /* obj var callbacks index cbtable */
392     lua_settable( L, -3 );
393     /* obj var callbacks */
394     lua_pop( L, 3 );
395     /* <empty stack> */
396
397     /* Do not move this before the lua specific code (it somehow changes
398      * the function in the stack to nil) */
399     p_callback->i_index = i_index;
400     p_callback->i_type = var_Type( *pp_obj, psz_var );
401     p_callback->L = lua_newthread( L ); /* Do we have to keep a reference to this thread somewhere to prevent garbage collection? */
402
403     var_AddCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
404     return 0;
405 }
406
407 static int vlclua_del_callback( lua_State *L )
408 {
409     vlclua_callback_t *p_callback;
410     bool b_found = false;
411     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
412     const char *psz_var = luaL_checkstring( L, 2 );
413     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
414     if( !lua_isfunction( L, 3 ) )
415         return vlclua_error( L );
416
417     /* obj var func data */
418     lua_getglobal( L, "vlc" );
419     /* obj var func data vlc */
420     lua_getfield( L, -1, "callbacks" );
421     if( lua_isnil( L, -1 ) )
422         return luaL_error( L, "Couldn't find matching callback." );
423     /* obj var func data vlc callbacks */
424     lua_remove( L, -2 );
425     /* obj var func data callbacks */
426     lua_pushnil( L );
427     /* obj var func data callbacks index */
428     while( lua_next( L, -2 ) )
429     {
430         /* obj var func data callbacks index value */
431         if( lua_isnumber( L, -2 ) )
432         {
433             lua_getfield( L, -1, "private2" );
434             /* obj var func data callbacks index value private2 */
435             if( lua_equal( L, 2, -1 ) ) /* var name is equal */
436             {
437                 lua_pop( L, 1 );
438                 /* obj var func data callbacks index value */
439                 lua_getfield( L, -1, "callback" );
440                 /* obj var func data callbacks index value callback */
441                 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
442                 {
443                     lua_pop( L, 1 );
444                     /* obj var func data callbacks index value */
445                     lua_getfield( L, -1, "data" ); /* callback data is equal */
446                     /* obj var func data callbacks index value data */
447                     if( lua_equal( L, 4, -1 ) )
448                     {
449                         vlc_object_t *p_obj2;
450                         lua_pop( L, 1 );
451                         /* obj var func data callbacks index value */
452                         lua_getfield( L, -1, "private1" );
453                         /* obj var func data callbacks index value private1 */
454                         p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
455                         if( p_obj2 == *pp_obj ) /* object is equal */
456                         {
457                             lua_pop( L, 1 );
458                             /* obj var func data callbacks index value */
459                             lua_getfield( L, -1, "private3" );
460                             /* obj var func data callbacks index value private3 */
461                             p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
462                             lua_pop( L, 2 );
463                             /* obj var func data callbacks index */
464                             b_found = true;
465                             break;
466                         }
467                         else
468                         {
469                             /* obj var func data callbacks index value private1 */
470                             lua_pop( L, 1 );
471                             /* obj var func data callbacks index value */
472                         }
473                     }
474                     else
475                     {
476                         /* obj var func data callbacks index value data */
477                         lua_pop( L, 1 );
478                         /* obj var func data callbacks index value */
479                     }
480                 }
481                 else
482                 {
483                     /* obj var func data callbacks index value callback */
484                     lua_pop( L, 1 );
485                     /* obj var func data callbacks index value */
486                 }
487             }
488             else
489             {
490                 /* obj var func data callbacks index value private2 */
491                 lua_pop( L, 1 );
492                 /* obj var func data callbacks index value */
493             }
494         }
495         /* obj var func data callbacks index value */
496         lua_pop( L, 1 );
497         /* obj var func data callbacks index */
498     }
499     if( b_found == false )
500         /* obj var func data callbacks */
501         return luaL_error( L, "Couldn't find matching callback." );
502     /* else */
503         /* obj var func data callbacks index*/
504
505     var_DelCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
506     free( p_callback );
507
508     /* obj var func data callbacks index */
509     lua_pushnil( L );
510     /* obj var func data callbacks index nil */
511     lua_settable( L, -3 ); /* delete the callback table entry */
512     /* obj var func data callbacks */
513     lua_pop( L, 5 );
514     /* <empty stack> */
515     return 0;
516 }
517
518 /*****************************************************************************
519  *
520  *****************************************************************************/
521 static const luaL_Reg vlclua_var_reg[] = {
522     { "get", vlclua_var_get },
523     { "get_list", vlclua_var_get_list },
524     { "set", vlclua_var_set },
525     { "create", vlclua_var_create },
526     { "add_callback", vlclua_add_callback },
527     { "del_callback", vlclua_del_callback },
528     { "command", vlclua_command },
529     { "libvlc_command", vlclua_libvlc_command },
530     { NULL, NULL }
531 };
532
533 void luaopen_variables( lua_State *L )
534 {
535     lua_newtable( L );
536     luaL_register( L, NULL, vlclua_var_reg );
537     lua_setfield( L, -2, "var" );
538 }