1 /*****************************************************************************
2 * variables.c: Generic lua<->vlc variables interface
3 *****************************************************************************
4 * Copyright (C) 2007-2008 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 &= 0xf0 )
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. );
80 int i_count = val.p_list->i_count;
82 lua_createtable( L, i_count, 0 );
83 for( i = 0; i < i_count; i++ )
85 lua_pushinteger( L, i+1 );
86 if( !vlclua_pushvalue( L, val.p_list->pi_types[i],
87 val.p_list->p_values[i] ) )
89 lua_settable( L, -3 );
99 static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
101 switch( i_type & 0xf0 )
106 val->b_bool = luaL_checkboolean( L, -1 );
108 case VLC_VAR_INTEGER:
109 val->i_int = luaL_checkint( L, -1 );
112 val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
115 val->f_float = luaL_checknumber( L, -1 );
119 double f = luaL_checknumber( L, -1 );
120 val->i_time = (int64_t)(f*1000000.);
123 case VLC_VAR_ADDRESS:
138 static int vlclua_var_get( lua_State *L )
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 var_Get( *pp_obj, psz_var, &val );
147 return vlclua_pushvalue( L, i_type, val );
150 static int vlclua_var_set( lua_State *L )
154 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
155 const char *psz_var = luaL_checkstring( L, 2 );
157 i_type = var_Type( *pp_obj, psz_var );
158 vlclua_tovalue( L, i_type, &val );
159 i_ret = var_Set( *pp_obj, psz_var, val );
161 return vlclua_push_ret( L, i_ret );
164 static int vlclua_var_get_list( lua_State *L )
168 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
169 const char *psz_var = luaL_checkstring( L, 2 );
170 int i_ret = var_Change( *pp_obj, psz_var, VLC_VAR_GETLIST, &val, &text );
171 if( i_ret < 0 ) return vlclua_push_ret( L, i_ret );
172 vlclua_pushvalue( L, VLC_VAR_LIST, val );
173 vlclua_pushvalue( L, VLC_VAR_LIST, text );
174 var_Change( *pp_obj, psz_var, VLC_VAR_FREELIST, &val, &text );
178 static int vlclua_command( lua_State *L )
180 vlc_object_t * p_this = vlclua_get_this( L );
181 const char *psz_name;
185 psz_name = luaL_checkstring( L, 1 );
186 psz_cmd = luaL_checkstring( L, 2 );
187 psz_arg = luaL_checkstring( L, 3 );
189 var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
192 lua_pushstring( L, psz_msg );
197 lua_pushstring( L, "" );
202 static int vlclua_libvlc_command( lua_State *L )
204 vlc_object_t * p_this = vlclua_get_this( L );
207 psz_cmd = luaL_checkstring( L, 1 );
208 val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
210 if( !var_Type( p_this->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
212 free( val_arg.psz_string );
213 return luaL_error( L, "libvlc's \"%s\" is not a command",
217 return vlclua_push_ret( L,
218 var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
221 int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
222 const char *psz_name )
225 if( lua_gettop( L ) > 1 ) return vlclua_error( L );
227 if( lua_gettop( L ) == 0 )
228 b_bool = !var_GetBool( p_obj, psz_name );
229 else /* lua_gettop( L ) == 1 */
231 b_bool = luaL_checkboolean( L, -1 )?true:false;
235 if( b_bool != var_GetBool( p_obj, psz_name ) )
236 var_SetBool( p_obj, psz_name, b_bool );
238 lua_pushboolean( L, b_bool );
242 static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
244 luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
245 return lua_topointer( L, narg );
255 static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
256 vlc_value_t oldval, vlc_value_t newval,
259 vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
260 lua_State *L = p_callback->L;
263 lua_getglobal( L, "vlc" );
265 lua_getfield( L, -1, "callbacks" );
269 lua_pushinteger( L, p_callback->i_index );
270 /* callbacks index */
271 lua_gettable( L, -2 );
272 /* callbacks callbacks[index] */
274 /* callbacks[index] */
275 lua_getfield( L, -1, "callback" );
276 /* callbacks[index] callback */
277 lua_pushstring( L, psz_var );
278 /* callbacks[index] callback var */
279 vlclua_pushvalue( L, p_callback->i_type, oldval );
280 /* callbacks[index] callback var oldval */
281 vlclua_pushvalue( L, p_callback->i_type, newval );
282 /* callbacks[index] callback var oldval newval */
283 lua_getfield( L, -5, "data" );
284 /* callbacks[index] callback var oldval newval data */
286 /* callback var oldval newval data */
288 if( lua_pcall( L, 4, 0, 0 ) )
291 const char *psz_err = lua_tostring( L, -1 );
292 msg_Err( p_this, "Error while runing lua interface callback: %s",
294 /* empty the stack (should only contain the error message) */
299 /* empty the stack (should already be empty) */
305 static int vlclua_add_callback( lua_State *L )
307 vlclua_callback_t *p_callback;
308 static int i_index = 0;
309 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
310 const char *psz_var = luaL_checkstring( L, 2 );
311 lua_settop( L, 4 ); /* makes sure that optional data arg is set */
312 if( !lua_isfunction( L, 3 ) )
313 return vlclua_error( L );
316 p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
318 return vlclua_error( L );
320 /* obj var func data */
321 lua_getglobal( L, "vlc" );
322 /* obj var func data vlc */
323 lua_getfield( L, -1, "callbacks" );
324 if( lua_isnil( L, -1 ) )
328 lua_setfield( L, -2, "callbacks" );
329 lua_getfield( L, -1, "callbacks" );
331 /* obj var func data vlc callbacks */
333 /* obj var func data callbacks */
334 lua_pushinteger( L, i_index );
335 /* obj var func data callbacks index */
337 /* obj var index func data callbacks */
339 /* obj var callbacks index func data */
340 lua_createtable( L, 0, 0 );
341 /* obj var callbacks index func data cbtable */
343 /* obj var callbacks index func cbtable data */
344 lua_setfield( L, -2, "data" );
345 /* obj var callbacks index func cbtable */
347 /* obj var callbacks index cbtable func */
348 lua_setfield( L, -2, "callback" );
349 /* obj var callbacks index cbtable */
350 lua_pushlightuserdata( L, *pp_obj ); /* will be needed in vlclua_del_callback */
351 /* obj var callbacks index cbtable p_obj */
352 lua_setfield( L, -2, "private1" );
353 /* obj var callbacks index cbtable */
354 lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
355 /* obj var callbacks index cbtable var */
356 lua_setfield( L, -2, "private2" );
357 /* obj var callbacks index cbtable */
358 lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
359 /* obj var callbacks index cbtable p_callback */
360 lua_setfield( L, -2, "private3" );
361 /* obj var callbacks index cbtable */
362 lua_settable( L, -3 );
363 /* obj var callbacks */
367 /* Do not move this before the lua specific code (it somehow changes
368 * the function in the stack to nil) */
369 p_callback->i_index = i_index;
370 p_callback->i_type = var_Type( *pp_obj, psz_var );
371 p_callback->L = lua_newthread( L ); /* Do we have to keep a reference to this thread somewhere to prevent garbage collection? */
373 var_AddCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
377 static int vlclua_del_callback( lua_State *L )
379 vlclua_callback_t *p_callback;
380 bool b_found = false;
381 vlc_object_t **pp_obj = luaL_checkudata( L, 1, "vlc_object" );
382 const char *psz_var = luaL_checkstring( L, 2 );
383 lua_settop( L, 4 ); /* makes sure that optional data arg is set */
384 if( !lua_isfunction( L, 3 ) )
385 return vlclua_error( L );
387 /* obj var func data */
388 lua_getglobal( L, "vlc" );
389 /* obj var func data vlc */
390 lua_getfield( L, -1, "callbacks" );
391 if( lua_isnil( L, -1 ) )
392 return luaL_error( L, "Couldn't find matching callback." );
393 /* obj var func data vlc callbacks */
395 /* obj var func data callbacks */
397 /* obj var func data callbacks index */
398 while( lua_next( L, -2 ) )
400 /* obj var func data callbacks index value */
401 if( lua_isnumber( L, -2 ) )
403 lua_getfield( L, -1, "private2" );
404 /* obj var func data callbacks index value private2 */
405 if( lua_equal( L, 2, -1 ) ) /* var name is equal */
408 /* obj var func data callbacks index value */
409 lua_getfield( L, -1, "callback" );
410 /* obj var func data callbacks index value callback */
411 if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
414 /* obj var func data callbacks index value */
415 lua_getfield( L, -1, "data" ); /* callback data is equal */
416 /* obj var func data callbacks index value data */
417 if( lua_equal( L, 4, -1 ) )
419 vlc_object_t *p_obj2;
421 /* obj var func data callbacks index value */
422 lua_getfield( L, -1, "private1" );
423 /* obj var func data callbacks index value private1 */
424 p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
425 if( p_obj2 == *pp_obj ) /* object is equal */
428 /* obj var func data callbacks index value */
429 lua_getfield( L, -1, "private3" );
430 /* obj var func data callbacks index value private3 */
431 p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
433 /* obj var func data callbacks index */
439 /* obj var func data callbacks index value private1 */
441 /* obj var func data callbacks index value */
446 /* obj var func data callbacks index value data */
448 /* obj var func data callbacks index value */
453 /* obj var func data callbacks index value callback */
455 /* obj var func data callbacks index value */
460 /* obj var func data callbacks index value private2 */
462 /* obj var func data callbacks index value */
465 /* obj var func data callbacks index value */
467 /* obj var func data callbacks index */
469 if( b_found == false )
470 /* obj var func data callbacks */
471 return luaL_error( L, "Couldn't find matching callback." );
473 /* obj var func data callbacks index*/
475 var_DelCallback( *pp_obj, psz_var, vlclua_callback, p_callback );
478 /* obj var func data callbacks index */
480 /* obj var func data callbacks index nil */
481 lua_settable( L, -3 ); /* delete the callback table entry */
482 /* obj var func data callbacks */
488 /*****************************************************************************
490 *****************************************************************************/
491 static const luaL_Reg vlclua_var_reg[] = {
492 { "get", vlclua_var_get },
493 { "get_list", vlclua_var_get_list },
494 { "set", vlclua_var_set },
495 { "add_callback", vlclua_add_callback },
496 { "del_callback", vlclua_del_callback },
497 { "command", vlclua_command },
498 { "libvlc_command", vlclua_libvlc_command },
502 void luaopen_variables( lua_State *L )
505 luaL_register( L, NULL, vlclua_var_reg );
506 lua_setfield( L, -2, "var" );