1 /*****************************************************************************
2 * variables.c: Generic lua<->vlc variables interface
3 *****************************************************************************
4 * Copyright (C) 2007-2010 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea at videolan tod org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
35 #include <vlc_common.h>
37 #include <lua.h> /* Low level lua C API */
38 #include <lauxlib.h> /* Higher level C API */
39 #include <lualib.h> /* Lua libs */
43 #include "variables.h"
46 /*****************************************************************************
48 *****************************************************************************/
49 int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val )
51 switch( i_type & VLC_VAR_CLASS )
57 lua_pushboolean( L, val.b_bool );
60 lua_pushinteger( L, val.i_int );
63 lua_pushstring( L, val.psz_string );
66 lua_pushnumber( L, val.f_float );
69 /* FIXME? (we're losing some precision, but does it really matter?) */
70 lua_pushnumber( L, ((double)val.i_time)/1000000. );
84 static int vlclua_pushlist( lua_State *L, vlc_list_t *p_list )
86 int i_count = p_list->i_count;
88 lua_createtable( L, i_count, 0 );
89 for( int i = 0; i < i_count; i++ )
91 lua_pushinteger( L, i+1 );
92 if( !vlclua_pushvalue( L, p_list->pi_types[i],
93 p_list->p_values[i] ) )
95 lua_settable( L, -3 );
100 static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
102 switch( i_type & VLC_VAR_CLASS )
107 val->b_bool = luaL_checkboolean( L, -1 );
109 case VLC_VAR_INTEGER:
110 val->i_int = luaL_checkint( L, -1 );
113 val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
116 val->f_float = luaL_checknumber( L, -1 );
120 double f = luaL_checknumber( L, -1 );
121 val->i_time = (int64_t)(f*1000000.);
124 case VLC_VAR_ADDRESS:
136 static int vlclua_var_get( lua_State *L )
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 )
146 return vlclua_pushvalue( L, i_type, val );
149 static int vlclua_var_set( lua_State *L )
153 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
154 const char *psz_var = luaL_checkstring( L, 2 );
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 );
160 return vlclua_push_ret( L, i_ret );
163 static int vlclua_var_create( lua_State *L )
165 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
166 const char *psz_var = luaL_checkstring( L, 2 );
168 switch( lua_type( L, 3 ) )
171 i_type = VLC_VAR_FLOAT;
174 i_type = VLC_VAR_BOOL;
177 i_type = VLC_VAR_STRING;
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 );
187 vlclua_tovalue( L, i_type, &val );
188 return vlclua_push_ret( L, var_Set( *pp_obj, psz_var, val ) );
191 static int vlclua_var_get_list( lua_State *L )
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 );
205 static int vlclua_command( lua_State *L )
207 vlc_object_t * p_this = vlclua_get_this( L );
208 const char *psz_name;
213 psz_name = luaL_checkstring( L, 1 );
214 psz_cmd = luaL_checkstring( L, 2 );
215 psz_arg = luaL_checkstring( L, 3 );
217 ret = var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
220 lua_pushstring( L, psz_msg );
225 lua_pushliteral( L, "" );
227 return vlclua_push_ret( L, ret ) + 1;
230 static int vlclua_libvlc_command( lua_State *L )
232 vlc_object_t * p_this = vlclua_get_this( L );
235 psz_cmd = luaL_checkstring( L, 1 );
236 val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
238 int i_type = var_Type( p_this->p_libvlc, psz_cmd );
239 if( ! (i_type & VLC_VAR_ISCOMMAND) )
241 free( val_arg.psz_string );
242 return luaL_error( L, "libvlc's \"%s\" is not a command",
246 return vlclua_push_ret( L,
247 var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
250 int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
251 const char *psz_name )
254 if( lua_gettop( L ) > 1 ) return vlclua_error( L );
256 if( lua_gettop( L ) == 0 )
257 b_bool = var_ToggleBool( p_obj, psz_name );
258 else /* lua_gettop( L ) == 1 */
260 b_bool = luaL_checkboolean( L, -1 );
262 if( b_bool != var_GetBool( p_obj, psz_name ) )
263 var_SetBool( p_obj, psz_name, b_bool );
266 lua_pushboolean( L, b_bool );
270 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
272 luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
273 return lua_topointer( L, narg );
283 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
284 vlc_value_t oldval, vlc_value_t newval,
287 vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
288 lua_State *L = p_callback->L;
291 lua_getglobal( L, "vlc" );
293 lua_getfield( L, -1, "callbacks" );
297 lua_pushinteger( L, p_callback->i_index );
298 /* callbacks index */
299 lua_gettable( L, -2 );
300 /* callbacks callbacks[index] */
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 */
314 /* callback var oldval newval data */
316 if( lua_pcall( L, 4, 0, 0 ) )
319 const char *psz_err = lua_tostring( L, -1 );
320 msg_Err( p_this, "Error while running lua interface callback: %s",
322 /* empty the stack (should only contain the error message) */
327 /* empty the stack (should already be empty) */
333 static int vlclua_add_callback( lua_State *L )
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 );
343 if( !pp_obj || !*pp_obj )
344 return vlclua_error( L );
346 /* Check variable type, in order to avoid PANIC */
347 switch( var_Type( *pp_obj, psz_var ) )
350 case VLC_VAR_INTEGER:
355 case VLC_VAR_ADDRESS:
359 return vlclua_error( L );
364 p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
366 return vlclua_error( L );
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 ) )
376 lua_setfield( L, -2, "callbacks" );
377 lua_getfield( L, -1, "callbacks" );
379 /* obj var func data vlc callbacks */
381 /* obj var func data callbacks */
382 lua_pushinteger( L, i_index );
383 /* obj var func data callbacks index */
385 /* obj var index func data callbacks */
387 /* obj var callbacks index func data */
388 lua_createtable( L, 0, 0 );
389 /* obj var callbacks index func data cbtable */
391 /* obj var callbacks index func cbtable data */
392 lua_setfield( L, -2, "data" );
393 /* obj var callbacks index func cbtable */
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 */
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? */
421 var_AddCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
425 static int vlclua_del_callback( lua_State *L )
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 );
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 */
443 /* obj var func data callbacks */
445 /* obj var func data callbacks index */
446 while( lua_next( L, -2 ) )
448 /* obj var func data callbacks index value */
449 if( lua_isnumber( L, -2 ) )
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 */
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 */
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 ) )
467 vlc_object_t *p_obj2;
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 */
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 );
481 /* obj var func data callbacks index */
487 /* obj var func data callbacks index value private1 */
489 /* obj var func data callbacks index value */
494 /* obj var func data callbacks index value data */
496 /* obj var func data callbacks index value */
501 /* obj var func data callbacks index value callback */
503 /* obj var func data callbacks index value */
508 /* obj var func data callbacks index value private2 */
510 /* obj var func data callbacks index value */
513 /* obj var func data callbacks index value */
515 /* obj var func data callbacks index */
517 if( b_found == false )
518 /* obj var func data callbacks */
519 return luaL_error( L, "Couldn't find matching callback." );
521 /* obj var func data callbacks index*/
523 var_DelCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
526 /* obj var func data callbacks index */
528 /* obj var func data callbacks index nil */
529 lua_settable( L, -3 ); /* delete the callback table entry */
530 /* obj var func data callbacks */
536 static int vlclua_trigger_callback( lua_State *L )
538 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
539 const char *psz_var = luaL_checkstring( L, 2 );
541 return vlclua_push_ret( L, var_TriggerCallback( *pp_obj, psz_var ) );
544 static int vlclua_inc_integer( lua_State *L )
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 );
550 lua_pushinteger( L, i_val );
554 static int vlclua_dec_integer( lua_State *L )
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 );
560 lua_pushinteger( L, i_val );
564 static int vlclua_countchoices( lua_State *L )
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 );
570 lua_pushinteger( L, i_count );
574 static int vlclua_togglebool( lua_State *L )
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 );
580 lua_pushboolean( L, b_val );
584 /*****************************************************************************
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 },
604 void luaopen_variables( lua_State *L )
607 luaL_register( L, NULL, vlclua_var_reg );
608 lua_setfield( L, -2, "var" );