]> git.sesse.net Git - vlc/blob - modules/misc/lua/libs/variables.c
lua: remove "bla ? true : false"
[vlc] / 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_GetBool( p_obj, psz_name );
258     else /* lua_gettop( L ) == 1 */
259     {
260         b_bool = luaL_checkboolean( L, -1 );
261         lua_pop( L, 1 );
262     }
263
264     if( b_bool != var_GetBool( p_obj, psz_name ) )
265         var_SetBool( p_obj, psz_name, b_bool );
266
267     lua_pushboolean( L, b_bool );
268     return 1;
269 }
270
271 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
272 {
273     luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
274     return lua_topointer( L, narg );
275 }
276
277 typedef struct
278 {
279     int i_index;
280     int i_type;
281     lua_State *L;
282 }   vlclua_callback_t;
283
284 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
285                             vlc_value_t oldval, vlc_value_t newval,
286                             void *p_data )
287 {
288     vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
289     lua_State *L = p_callback->L;
290
291     /* <empty stack> */
292     lua_getglobal( L, "vlc" );
293     /* vlc */
294     lua_getfield( L, -1, "callbacks" );
295     /* vlc callbacks */
296     lua_remove( L, -2 );
297     /* callbacks */
298     lua_pushinteger( L, p_callback->i_index );
299     /* callbacks index */
300     lua_gettable( L, -2 );
301     /* callbacks callbacks[index] */
302     lua_remove( L, -2 );
303     /* callbacks[index] */
304     lua_getfield( L, -1, "callback" );
305     /* callbacks[index] callback */
306     lua_pushstring( L, psz_var );
307     /* callbacks[index] callback var */
308     vlclua_pushvalue( L, p_callback->i_type, oldval );
309     /* callbacks[index] callback var oldval */
310     vlclua_pushvalue( L, p_callback->i_type, newval );
311     /* callbacks[index] callback var oldval newval */
312     lua_getfield( L, -5, "data" );
313     /* callbacks[index] callback var oldval newval data */
314     lua_remove( L, -6 );
315     /* callback var oldval newval data */
316
317     if( lua_pcall( L, 4, 0, 0 ) )
318     {
319         /* errormessage */
320         const char *psz_err = lua_tostring( L, -1 );
321         msg_Err( p_this, "Error while running lua interface callback: %s",
322                  psz_err );
323         /* empty the stack (should only contain the error message) */
324         lua_settop( L, 0 );
325         return VLC_EGENERIC;
326     }
327
328     /* empty the stack (should already be empty) */
329     lua_settop( L, 0 );
330
331     return VLC_SUCCESS;
332 }
333
334 static int vlclua_add_callback( lua_State *L )
335 {
336     vlclua_callback_t *p_callback;
337     static int i_index = 0;
338     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
339     const char *psz_var = luaL_checkstring( L, 2 );
340     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
341     if( !lua_isfunction( L, 3 ) )
342         return vlclua_error( L );
343
344     if( !pp_obj || !*pp_obj )
345         return vlclua_error( L );
346
347     /* Check variable type, in order to avoid PANIC */
348     switch( var_Type( *pp_obj, psz_var ) )
349     {
350         case VLC_VAR_BOOL:
351         case VLC_VAR_INTEGER:
352         case VLC_VAR_STRING:
353         case VLC_VAR_FLOAT:
354         case VLC_VAR_TIME:
355             break;
356         case VLC_VAR_ADDRESS:
357         case VLC_VAR_VOID:
358         case VLC_VAR_MUTEX:
359         default:
360             return vlclua_error( L );
361     }
362
363     i_index++;
364
365     p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
366     if( !p_callback )
367         return vlclua_error( L );
368
369     /* obj var func data */
370     lua_getglobal( L, "vlc" );
371     /* obj var func data vlc */
372     lua_getfield( L, -1, "callbacks" );
373     if( lua_isnil( L, -1 ) )
374     {
375         lua_pop( L, 1 );
376         lua_newtable( L );
377         lua_setfield( L, -2, "callbacks" );
378         lua_getfield( L, -1, "callbacks" );
379     }
380     /* obj var func data vlc callbacks */
381     lua_remove( L, -2 );
382     /* obj var func data callbacks */
383     lua_pushinteger( L, i_index );
384     /* obj var func data callbacks index */
385     lua_insert( L, -4 );
386     /* obj var index func data callbacks */
387     lua_insert( L, -4 );
388     /* obj var callbacks index func data */
389     lua_createtable( L, 0, 0 );
390     /* obj var callbacks index func data cbtable */
391     lua_insert( L, -2 );
392     /* obj var callbacks index func cbtable data */
393     lua_setfield( L, -2, "data" );
394     /* obj var callbacks index func cbtable */
395     lua_insert( L, -2 );
396     /* obj var callbacks index cbtable func */
397     lua_setfield( L, -2, "callback" );
398     /* obj var callbacks index cbtable */
399     lua_pushlightuserdata( L, *pp_obj ); /* will be needed in vlclua_del_callback */
400     /* obj var callbacks index cbtable p_obj */
401     lua_setfield( L, -2, "private1" );
402     /* obj var callbacks index cbtable */
403     lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
404     /* obj var callbacks index cbtable var */
405     lua_setfield( L, -2, "private2" );
406     /* obj var callbacks index cbtable */
407     lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
408     /* obj var callbacks index cbtable p_callback */
409     lua_setfield( L, -2, "private3" );
410     /* obj var callbacks index cbtable */
411     lua_settable( L, -3 );
412     /* obj var callbacks */
413     lua_pop( L, 3 );
414     /* <empty stack> */
415
416     /* Do not move this before the lua specific code (it somehow changes
417      * the function in the stack to nil) */
418     p_callback->i_index = i_index;
419     p_callback->i_type = var_Type( *pp_obj, psz_var );
420     p_callback->L = lua_newthread( L ); /* Do we have to keep a reference to this thread somewhere to prevent garbage collection? */
421
422     var_AddCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
423     return 0;
424 }
425
426 static int vlclua_del_callback( lua_State *L )
427 {
428     vlclua_callback_t *p_callback;
429     bool b_found = false;
430     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
431     const char *psz_var = luaL_checkstring( L, 2 );
432     lua_settop( L, 4 ); /* makes sure that optional data arg is set */
433     if( !lua_isfunction( L, 3 ) )
434         return vlclua_error( L );
435
436     /* obj var func data */
437     lua_getglobal( L, "vlc" );
438     /* obj var func data vlc */
439     lua_getfield( L, -1, "callbacks" );
440     if( lua_isnil( L, -1 ) )
441         return luaL_error( L, "Couldn't find matching callback." );
442     /* obj var func data vlc callbacks */
443     lua_remove( L, -2 );
444     /* obj var func data callbacks */
445     lua_pushnil( L );
446     /* obj var func data callbacks index */
447     while( lua_next( L, -2 ) )
448     {
449         /* obj var func data callbacks index value */
450         if( lua_isnumber( L, -2 ) )
451         {
452             lua_getfield( L, -1, "private2" );
453             /* obj var func data callbacks index value private2 */
454             if( lua_equal( L, 2, -1 ) ) /* var name is equal */
455             {
456                 lua_pop( L, 1 );
457                 /* obj var func data callbacks index value */
458                 lua_getfield( L, -1, "callback" );
459                 /* obj var func data callbacks index value callback */
460                 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
461                 {
462                     lua_pop( L, 1 );
463                     /* obj var func data callbacks index value */
464                     lua_getfield( L, -1, "data" ); /* callback data is equal */
465                     /* obj var func data callbacks index value data */
466                     if( lua_equal( L, 4, -1 ) )
467                     {
468                         vlc_object_t *p_obj2;
469                         lua_pop( L, 1 );
470                         /* obj var func data callbacks index value */
471                         lua_getfield( L, -1, "private1" );
472                         /* obj var func data callbacks index value private1 */
473                         p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
474                         if( p_obj2 == *pp_obj ) /* object is equal */
475                         {
476                             lua_pop( L, 1 );
477                             /* obj var func data callbacks index value */
478                             lua_getfield( L, -1, "private3" );
479                             /* obj var func data callbacks index value private3 */
480                             p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
481                             lua_pop( L, 2 );
482                             /* obj var func data callbacks index */
483                             b_found = true;
484                             break;
485                         }
486                         else
487                         {
488                             /* obj var func data callbacks index value private1 */
489                             lua_pop( L, 1 );
490                             /* obj var func data callbacks index value */
491                         }
492                     }
493                     else
494                     {
495                         /* obj var func data callbacks index value data */
496                         lua_pop( L, 1 );
497                         /* obj var func data callbacks index value */
498                     }
499                 }
500                 else
501                 {
502                     /* obj var func data callbacks index value callback */
503                     lua_pop( L, 1 );
504                     /* obj var func data callbacks index value */
505                 }
506             }
507             else
508             {
509                 /* obj var func data callbacks index value private2 */
510                 lua_pop( L, 1 );
511                 /* obj var func data callbacks index value */
512             }
513         }
514         /* obj var func data callbacks index value */
515         lua_pop( L, 1 );
516         /* obj var func data callbacks index */
517     }
518     if( b_found == false )
519         /* obj var func data callbacks */
520         return luaL_error( L, "Couldn't find matching callback." );
521     /* else */
522         /* obj var func data callbacks index*/
523
524     var_DelCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
525     free( p_callback );
526
527     /* obj var func data callbacks index */
528     lua_pushnil( L );
529     /* obj var func data callbacks index nil */
530     lua_settable( L, -3 ); /* delete the callback table entry */
531     /* obj var func data callbacks */
532     lua_pop( L, 5 );
533     /* <empty stack> */
534     return 0;
535 }
536
537 static int vlclua_trigger_callback( lua_State *L )
538 {
539     vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
540     const char *psz_var = luaL_checkstring( L, 2 );
541
542     return vlclua_push_ret( L, var_TriggerCallback( *pp_obj, psz_var ) );
543 }
544
545 /*****************************************************************************
546  *
547  *****************************************************************************/
548 static const luaL_Reg vlclua_var_reg[] = {
549     { "get", vlclua_var_get },
550     { "get_list", vlclua_var_get_list },
551     { "set", vlclua_var_set },
552     { "create", vlclua_var_create },
553     { "add_callback", vlclua_add_callback },
554     { "del_callback", vlclua_del_callback },
555     { "trigger_callback", vlclua_trigger_callback },
556     { "command", vlclua_command },
557     { "libvlc_command", vlclua_libvlc_command },
558     { NULL, NULL }
559 };
560
561 void luaopen_variables( lua_State *L )
562 {
563     lua_newtable( L );
564     luaL_register( L, NULL, vlclua_var_reg );
565     lua_setfield( L, -2, "var" );
566 }