lua: implement var_ToggleBool and var_CountChoices.
[vlc.git] / modules / misc / lua / libs / variables.c
1 /*****************************************************************************
2  * variables.c: Generic lua<->vlc variables interface
3  *****************************************************************************
4  * Copyright (C) 2007-2010 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 &= VLC_VAR_CLASS )
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         default:
79             vlclua_error( L );
80     }
81     return 1;
82 }
83
84 static int vlclua_pushlist( lua_State *L, vlc_list_t *p_list )
85 {
86     int i_count = p_list->i_count;
87
88     lua_createtable( L, i_count, 0 );
89     for( int i = 0; i < i_count; i++ )
90     {
91         lua_pushinteger( L, i+1 );
92         if( !vlclua_pushvalue( L, p_list->pi_types[i],
93                                p_list->p_values[i] ) )
94              lua_pushnil( L );
95         lua_settable( L, -3 );
96     }
97     return 1;
98 }
99
100 static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
101 {
102     switch( i_type & VLC_VAR_CLASS )
103     {
104         case VLC_VAR_VOID:
105             break;
106         case VLC_VAR_BOOL:
107             val->b_bool = luaL_checkboolean( L, -1 );
108             break;
109         case VLC_VAR_INTEGER:
110             val->i_int = luaL_checkint( L, -1 );
111             break;
112         case VLC_VAR_STRING:
113             val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
114             break;
115         case VLC_VAR_FLOAT:
116             val->f_float = luaL_checknumber( L, -1 );
117             break;
118         case VLC_VAR_TIME:
119             {
120                 double f = luaL_checknumber( L, -1 );
121                 val->i_time = (int64_t)(f*1000000.);
122             }
123             break;
124         case VLC_VAR_ADDRESS:
125             vlclua_error( L );
126             break;
127         case VLC_VAR_MUTEX:
128             vlclua_error( L );
129             break;
130         default:
131             vlclua_error( L );
132     }
133     return 1;
134 }
135
136 static int vlclua_var_get( lua_State *L )
137 {
138     int i_type;
139     vlc_value_t val;
140     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
141     const char *psz_var = luaL_checkstring( L, 2 );
142     i_type = var_Type( *pp_obj, psz_var );
143     if( var_Get( *pp_obj, psz_var, &val ) != VLC_SUCCESS )
144         return 0;
145     lua_pop( L, 2 );
146     return vlclua_pushvalue( L, i_type, val );
147 }
148
149 static int vlclua_var_set( lua_State *L )
150 {
151     int i_type;
152     vlc_value_t val;
153     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
154     const char *psz_var = luaL_checkstring( L, 2 );
155     int i_ret;
156     i_type = var_Type( *pp_obj, psz_var );
157     vlclua_tovalue( L, i_type, &val );
158     i_ret = var_Set( *pp_obj, psz_var, val );
159     lua_pop( L, 3 );
160     return vlclua_push_ret( L, i_ret );
161 }
162
163 static int vlclua_var_create( lua_State *L )
164 {
165     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
166     const char *psz_var = luaL_checkstring( L, 2 );
167     int i_type;
168     switch( lua_type( L, 3 ) )
169     {
170         case LUA_TNUMBER:
171             i_type = VLC_VAR_FLOAT;
172             break;
173         case LUA_TBOOLEAN:
174             i_type = VLC_VAR_BOOL;
175             break;
176         case LUA_TSTRING:
177             i_type = VLC_VAR_STRING;
178             break;
179         default:
180             return 0;
181     }
182
183     int i_ret = var_Create( *pp_obj, psz_var, i_type );
184     if( i_ret != VLC_SUCCESS )
185         return vlclua_push_ret( L, i_ret );
186     vlc_value_t val;
187     vlclua_tovalue( L, i_type, &val );
188     return vlclua_push_ret( L, var_Set( *pp_obj, psz_var, val ) );
189 }
190
191 static int vlclua_var_get_list( lua_State *L )
192 {
193     vlc_value_t val;
194     vlc_value_t text;
195     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
196     const char *psz_var = luaL_checkstring( L, 2 );
197     int i_ret = var_Change( *pp_obj, psz_var, VLC_VAR_GETLIST, &val, &text );
198     if( i_ret < 0 ) return vlclua_push_ret( L, i_ret );
199     vlclua_pushlist( L, val.p_list );
200     vlclua_pushlist( L, text.p_list );
201     var_FreeList( &val, &text );
202     return 2;
203 }
204
205 static int vlclua_command( lua_State *L )
206 {
207     vlc_object_t * p_this = vlclua_get_this( L );
208     const char *psz_name;
209     const char *psz_cmd;
210     const char *psz_arg;
211     char *psz_msg;
212     int ret;
213     psz_name = luaL_checkstring( L, 1 );
214     psz_cmd = luaL_checkstring( L, 2 );
215     psz_arg = luaL_checkstring( L, 3 );
216     lua_pop( L, 3 );
217     ret = var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
218     if( psz_msg )
219     {
220         lua_pushstring( L, psz_msg );
221         free( psz_msg );
222     }
223     else
224     {
225         lua_pushliteral( L, "" );
226     }
227     return vlclua_push_ret( L, ret ) + 1;
228 }
229
230 static int vlclua_libvlc_command( lua_State *L )
231 {
232     vlc_object_t * p_this = vlclua_get_this( L );
233     const char *psz_cmd;
234     vlc_value_t val_arg;
235     psz_cmd = luaL_checkstring( L, 1 );
236     val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
237     lua_pop( L, 2 );
238     int i_type = var_Type( p_this->p_libvlc, psz_cmd );
239     if( ! (i_type & VLC_VAR_ISCOMMAND) )
240     {
241         free( val_arg.psz_string );
242         return luaL_error( L, "libvlc's \"%s\" is not a command",
243                            psz_cmd );
244     }
245
246     return vlclua_push_ret( L,
247                             var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
248 }
249
250 int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
251                                 const char *psz_name )
252 {
253     bool b_bool;
254     if( lua_gettop( L ) > 1 ) return vlclua_error( L );
255
256     if( lua_gettop( L ) == 0 )
257         b_bool = var_ToggleBool( p_obj, psz_name );
258     else /* lua_gettop( L ) == 1 */
259     {
260         b_bool = luaL_checkboolean( L, -1 );
261         lua_pop( L, 1 );
262         if( b_bool != var_GetBool( p_obj, psz_name ) )
263             var_SetBool( p_obj, psz_name, b_bool );
264     }
265
266     lua_pushboolean( L, b_bool );
267     return 1;
268 }
269
270 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
271 {
272     luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
273     return lua_topointer( L, narg );
274 }
275
276 typedef struct
277 {
278     int i_index;
279     int i_type;
280     lua_State *L;
281 }   vlclua_callback_t;
282
283 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
284                             vlc_value_t oldval, vlc_value_t newval,
285                             void *p_data )
286 {
287     vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
288     lua_State *L = p_callback->L;
289
290     /* <empty stack> */
291     lua_getglobal( L, "vlc" );
292     /* vlc */
293     lua_getfield( L, -1, "callbacks" );
294     /* vlc callbacks */
295     lua_remove( L, -2 );
296     /* callbacks */
297     lua_pushinteger( L, p_callback->i_index );
298     /* callbacks index */
299     lua_gettable( L, -2 );
300     /* callbacks callbacks[index] */
301     lua_remove( L, -2 );
302     /* callbacks[index] */
303     lua_getfield( L, -1, "callback" );
304     /* callbacks[index] callback */
305     lua_pushstring( L, psz_var );
306     /* callbacks[index] callback var */
307     vlclua_pushvalue( L, p_callback->i_type, oldval );
308     /* callbacks[index] callback var oldval */
309     vlclua_pushvalue( L, p_callback->i_type, newval );
310     /* callbacks[index] callback var oldval newval */
311     lua_getfield( L, -5, "data" );
312     /* callbacks[index] callback var oldval newval data */
313     lua_remove( L, -6 );
314     /* callback var oldval newval data */
315
316     if( lua_pcall( L, 4, 0, 0 ) )
317     {
318         /* errormessage */
319         const char *psz_err = lua_tostring( L, -1 );
320         msg_Err( p_this, "Error while running lua interface callback: %s",
321                  psz_err );
322         /* empty the stack (should only contain the error message) */
323         lua_settop( L, 0 );
324         return VLC_EGENERIC;
325     }
326
327     /* empty the stack (should already be empty) */
328     lua_settop( L, 0 );
329
330     return VLC_SUCCESS;
331 }
332
333 static int vlclua_add_callback( lua_State *L )
334 {
335     vlclua_callback_t *p_callback;
336     static int i_index = 0;
337     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
338     const char *psz_var = luaL_checkstring( L, 2 );
339     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
340     if( !lua_isfunction( L, 3 ) )
341         return vlclua_error( L );
342
343     if( !pp_obj || !*pp_obj )
344         return vlclua_error( L );
345
346     /* Check variable type, in order to avoid PANIC */
347     switch( var_Type( *pp_obj, psz_var ) )
348     {
349         case VLC_VAR_BOOL:
350         case VLC_VAR_INTEGER:
351         case VLC_VAR_STRING:
352         case VLC_VAR_FLOAT:
353         case VLC_VAR_TIME:
354             break;
355         case VLC_VAR_ADDRESS:
356         case VLC_VAR_VOID:
357         case VLC_VAR_MUTEX:
358         default:
359             return vlclua_error( L );
360     }
361
362     i_index++;
363
364     p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
365     if( !p_callback )
366         return vlclua_error( L );
367
368     /* obj var func data */
369     lua_getglobal( L, "vlc" );
370     /* obj var func data vlc */
371     lua_getfield( L, -1, "callbacks" );
372     if( lua_isnil( L, -1 ) )
373     {
374         lua_pop( L, 1 );
375         lua_newtable( L );
376         lua_setfield( L, -2, "callbacks" );
377         lua_getfield( L, -1, "callbacks" );
378     }
379     /* obj var func data vlc callbacks */
380     lua_remove( L, -2 );
381     /* obj var func data callbacks */
382     lua_pushinteger( L, i_index );
383     /* obj var func data callbacks index */
384     lua_insert( L, -4 );
385     /* obj var index func data callbacks */
386     lua_insert( L, -4 );
387     /* obj var callbacks index func data */
388     lua_createtable( L, 0, 0 );
389     /* obj var callbacks index func data cbtable */
390     lua_insert( L, -2 );
391     /* obj var callbacks index func cbtable data */
392     lua_setfield( L, -2, "data" );
393     /* obj var callbacks index func cbtable */
394     lua_insert( L, -2 );
395     /* obj var callbacks index cbtable func */
396     lua_setfield( L, -2, "callback" );
397     /* obj var callbacks index cbtable */
398     lua_pushlightuserdata( L, *pp_obj ); /* will be needed in vlclua_del_callback */
399     /* obj var callbacks index cbtable p_obj */
400     lua_setfield( L, -2, "private1" );
401     /* obj var callbacks index cbtable */
402     lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
403     /* obj var callbacks index cbtable var */
404     lua_setfield( L, -2, "private2" );
405     /* obj var callbacks index cbtable */
406     lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
407     /* obj var callbacks index cbtable p_callback */
408     lua_setfield( L, -2, "private3" );
409     /* obj var callbacks index cbtable */
410     lua_settable( L, -3 );
411     /* obj var callbacks */
412     lua_pop( L, 3 );
413     /* <empty stack> */
414
415     /* Do not move this before the lua specific code (it somehow changes
416      * the function in the stack to nil) */
417     p_callback->i_index = i_index;
418     p_callback->i_type = var_Type( *pp_obj, psz_var );
419     p_callback->L = lua_newthread( L ); /* Do we have to keep a reference to this thread somewhere to prevent garbage collection? */
420
421     var_AddCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
422     return 0;
423 }
424
425 static int vlclua_del_callback( lua_State *L )
426 {
427     vlclua_callback_t *p_callback;
428     bool b_found = false;
429     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
430     const char *psz_var = luaL_checkstring( L, 2 );
431     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
432     if( !lua_isfunction( L, 3 ) )
433         return vlclua_error( L );
434
435     /* obj var func data */
436     lua_getglobal( L, "vlc" );
437     /* obj var func data vlc */
438     lua_getfield( L, -1, "callbacks" );
439     if( lua_isnil( L, -1 ) )
440         return luaL_error( L, "Couldn't find matching callback." );
441     /* obj var func data vlc callbacks */
442     lua_remove( L, -2 );
443     /* obj var func data callbacks */
444     lua_pushnil( L );
445     /* obj var func data callbacks index */
446     while( lua_next( L, -2 ) )
447     {
448         /* obj var func data callbacks index value */
449         if( lua_isnumber( L, -2 ) )
450         {
451             lua_getfield( L, -1, "private2" );
452             /* obj var func data callbacks index value private2 */
453             if( lua_equal( L, 2, -1 ) ) /* var name is equal */
454             {
455                 lua_pop( L, 1 );
456                 /* obj var func data callbacks index value */
457                 lua_getfield( L, -1, "callback" );
458                 /* obj var func data callbacks index value callback */
459                 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
460                 {
461                     lua_pop( L, 1 );
462                     /* obj var func data callbacks index value */
463                     lua_getfield( L, -1, "data" ); /* callback data is equal */
464                     /* obj var func data callbacks index value data */
465                     if( lua_equal( L, 4, -1 ) )
466                     {
467                         vlc_object_t *p_obj2;
468                         lua_pop( L, 1 );
469                         /* obj var func data callbacks index value */
470                         lua_getfield( L, -1, "private1" );
471                         /* obj var func data callbacks index value private1 */
472                         p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
473                         if( p_obj2 == *pp_obj ) /* object is equal */
474                         {
475                             lua_pop( L, 1 );
476                             /* obj var func data callbacks index value */
477                             lua_getfield( L, -1, "private3" );
478                             /* obj var func data callbacks index value private3 */
479                             p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
480                             lua_pop( L, 2 );
481                             /* obj var func data callbacks index */
482                             b_found = true;
483                             break;
484                         }
485                         else
486                         {
487                             /* obj var func data callbacks index value private1 */
488                             lua_pop( L, 1 );
489                             /* obj var func data callbacks index value */
490                         }
491                     }
492                     else
493                     {
494                         /* obj var func data callbacks index value data */
495                         lua_pop( L, 1 );
496                         /* obj var func data callbacks index value */
497                     }
498                 }
499                 else
500                 {
501                     /* obj var func data callbacks index value callback */
502                     lua_pop( L, 1 );
503                     /* obj var func data callbacks index value */
504                 }
505             }
506             else
507             {
508                 /* obj var func data callbacks index value private2 */
509                 lua_pop( L, 1 );
510                 /* obj var func data callbacks index value */
511             }
512         }
513         /* obj var func data callbacks index value */
514         lua_pop( L, 1 );
515         /* obj var func data callbacks index */
516     }
517     if( b_found == false )
518         /* obj var func data callbacks */
519         return luaL_error( L, "Couldn't find matching callback." );
520     /* else */
521         /* obj var func data callbacks index*/
522
523     var_DelCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
524     free( p_callback );
525
526     /* obj var func data callbacks index */
527     lua_pushnil( L );
528     /* obj var func data callbacks index nil */
529     lua_settable( L, -3 ); /* delete the callback table entry */
530     /* obj var func data callbacks */
531     lua_pop( L, 5 );
532     /* <empty stack> */
533     return 0;
534 }
535
536 static int vlclua_trigger_callback( lua_State *L )
537 {
538     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
539     const char *psz_var = luaL_checkstring( L, 2 );
540
541     return vlclua_push_ret( L, var_TriggerCallback( *pp_obj, psz_var ) );
542 }
543
544 static int vlclua_inc_integer( lua_State *L )
545 {
546     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
547     const char *psz_var = luaL_checkstring( L, 2 );
548     int64_t i_val = var_IncInteger( *pp_obj, psz_var );
549
550     lua_pushinteger( L, i_val );
551     return 1;
552 }
553
554 static int vlclua_dec_integer( lua_State *L )
555 {
556     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
557     const char *psz_var = luaL_checkstring( L, 2 );
558     int64_t i_val = var_DecInteger( *pp_obj, psz_var );
559
560     lua_pushinteger( L, i_val );
561     return 1;
562 }
563
564 static int vlclua_countchoices( lua_State *L )
565 {
566     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
567     const char *psz_var = luaL_checkstring( L, 2 );
568     int i_count = var_CountChoices( *pp_obj, psz_var );
569
570     lua_pushinteger( L, i_count );
571     return 1;
572 }
573
574 static int vlclua_togglebool( lua_State *L )
575 {
576     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
577     const char *psz_var = luaL_checkstring( L, 2 );
578     bool b_val = var_ToggleBool( *pp_obj, psz_var );
579
580     lua_pushboolean( L, b_val );
581     return 1;
582 }
583
584 /*****************************************************************************
585  *
586  *****************************************************************************/
587 static const luaL_Reg vlclua_var_reg[] = {
588     { "get", vlclua_var_get },
589     { "get_list", vlclua_var_get_list },
590     { "set", vlclua_var_set },
591     { "create", vlclua_var_create },
592     { "add_callback", vlclua_add_callback },
593     { "del_callback", vlclua_del_callback },
594     { "trigger_callback", vlclua_trigger_callback },
595     { "command", vlclua_command },
596     { "libvlc_command", vlclua_libvlc_command },
597     { "inc_integer", vlclua_inc_integer },
598     { "dec_integer", vlclua_dec_integer },
599     { "count_choices", vlclua_countchoices },
600     { "toggle_bool", vlclua_togglebool },
601     { NULL, NULL }
602 };
603
604 void luaopen_variables( lua_State *L )
605 {
606     lua_newtable( L );
607     luaL_register( L, NULL, vlclua_var_reg );
608     lua_setfield( L, -2, "var" );
609 }