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