From de31813fc07c398e906346df090bf4680212a85c Mon Sep 17 00:00:00 2001 From: Antoine Cellerier Date: Sun, 4 Nov 2007 21:42:27 +0000 Subject: [PATCH] Add a new type of VLC Lua module: Interfaces. Two things led me to add this new type of Lua modules: * Most interfaces duplicate code from one another (rc and telnet both deal with sockets, rc and hotkeys duplicate VLC interfacing code) * It's easier to code in Lua than in C (for high level stuff) (* Users can code their own module easily, all they need is a text editor.) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Most of the changes in modules/misc/ are Lua wrappers for different C APIs in VLC. I'd say that 90% of those wrappers won't change, the other 10% might need small changes (mostly to simplify the C code). The VLC Lua "interface" module will look for lua scripts in the *share*/luaintf/ directory. Files in *share*/luaintf/modules/ are lua modules which can be used by multiple VLC Lua modules (using require "modulename"). This commit includes 4 Lua interface modules: * dummy.lua: the default module. Prints error messages and quits. * rc.lua: equivalent to the legacy rc.c module. Also includes a bunch of new features (+ multiple socket connections at a time work!). See file for more details. * telnet.lua: equivalent to the legacy telnet.c module. See file for more details. * hotkeys.lua: a rewrite of the hotkey handling module. This module is still experimental. I'll need to change VLC core hotkeys stuff to make it work like I want (ie: 1 hotkey triggers 1 action instead of the current 1 action can have 1 hotkey). This version executes 3 dummy actions when keys 'q', 'w' or 'x' are pressed. What's left to do: * Port the http interface plugin to this framework (at least for the macros/rpn part ... using à la PHP would be way easier than what we currently have). * Finish work on the hotkeys module. * Code a real telnet/rc module with autocompletion and all the cool features usually found in a telnet/terminal interface. * Trash the legacy C modules. Stuff to test (which I can't test myself): * Win32 and Mac OS X specific changes to Makefile.am * Console interface under Win32. I expect it not to work. Other stuff included in this changeset are: * Code cleanup (I'm sure that it's still possible to simplify some of the old lua bindings code). * That's pretty much it in fact :/ --- Makefile.am | 16 + modules/misc/lua/Modules.am | 2 +- modules/misc/lua/callbacks.c | 277 ++++++++ modules/misc/lua/configuration.c | 106 +++ modules/misc/lua/intf.c | 897 +++++++++++++++++++++++++ modules/misc/lua/luaplaylist.c | 414 ------------ modules/misc/lua/{luameta.c => meta.c} | 142 ++-- modules/misc/lua/net.c | 207 ++++++ modules/misc/lua/objects.c | 218 ++++++ modules/misc/lua/playlist.c | 294 ++++++++ modules/misc/lua/variables.c | 212 ++++++ modules/misc/lua/vlc.c | 712 ++++++++++++++++++++ modules/misc/lua/vlc.h | 202 ++++++ modules/misc/lua/vlclua.c | 434 ------------ modules/misc/lua/vlclua.h | 114 ---- modules/misc/lua/vlm.c | 95 +++ share/Makefile.am | 8 +- share/luaintf/dummy.lua | 12 + share/luaintf/hotkeys.lua | 119 ++++ share/luaintf/modules/common.lua | 72 ++ share/luaintf/modules/host.lua | 350 ++++++++++ share/luaintf/rc.lua | 521 ++++++++++++++ share/luaintf/telnet.lua | 204 ++++++ 23 files changed, 4600 insertions(+), 1028 deletions(-) create mode 100644 modules/misc/lua/callbacks.c create mode 100644 modules/misc/lua/configuration.c create mode 100644 modules/misc/lua/intf.c delete mode 100644 modules/misc/lua/luaplaylist.c rename modules/misc/lua/{luameta.c => meta.c} (69%) create mode 100644 modules/misc/lua/net.c create mode 100644 modules/misc/lua/objects.c create mode 100644 modules/misc/lua/playlist.c create mode 100644 modules/misc/lua/variables.c create mode 100644 modules/misc/lua/vlc.c create mode 100644 modules/misc/lua/vlc.h delete mode 100644 modules/misc/lua/vlclua.c delete mode 100644 modules/misc/lua/vlclua.h create mode 100644 modules/misc/lua/vlm.c create mode 100644 share/luaintf/dummy.lua create mode 100644 share/luaintf/hotkeys.lua create mode 100644 share/luaintf/modules/common.lua create mode 100644 share/luaintf/modules/host.lua create mode 100644 share/luaintf/rc.lua create mode 100644 share/luaintf/telnet.lua diff --git a/Makefile.am b/Makefile.am index 7b7874a0eb..42fe2163a0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -433,6 +433,14 @@ VLC-release.app: vlc for i in $(srcdir)/share/luameta/*.* ; do \ $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luameta/`basename $${i}` ; \ done ; \ + $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf + for i in $(srcdir)/share/luaintf/*.* ; do \ + $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/`basename $${i}` ; \ + done ; \ + $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/modules + for i in $(srcdir)/share/luaintf/modules/*.* ; do \ + $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/modules/`basename $${i}` ; \ + done ; \ $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/dialogs $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/js $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/old @@ -733,6 +741,14 @@ package-win32-base-debug: package-win-common for i in $(srcdir)/share/luameta/*.* ; do \ $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luameta/`basename $${i}` ; \ done ; + $(INSTALL) -d $(top_builddir)/vlc-${VERSION}/share/luaintf + for i in $(srcdir)/share/luaintf/*.* ; do \ + $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luaintf/`basename $${i}` ; \ + done ; + $(INSTALL) -d $(top_builddir)/vlc-${VERSION}/share/luaintf/modules + for i in $(srcdir)/share/luaintf/modules/*.* ; do \ + $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luaintf/modules/`basename $${i}` ; \ + done ; mkdir -p "$(top_builddir)/vlc-${VERSION}/osdmenu" cp $(srcdir)/share/osdmenu/*.* "$(top_builddir)/vlc-${VERSION}/osdmenu" diff --git a/modules/misc/lua/Modules.am b/modules/misc/lua/Modules.am index d22ed9b86a..f01c536805 100644 --- a/modules/misc/lua/Modules.am +++ b/modules/misc/lua/Modules.am @@ -1 +1 @@ -SOURCES_lua = luaplaylist.c luameta.c vlclua.c vlclua.h +SOURCES_lua = playlist.c meta.c intf.c vlc.c vlc.h callbacks.c objects.c variables.c configuration.c net.c vlm.c diff --git a/modules/misc/lua/callbacks.c b/modules/misc/lua/callbacks.c new file mode 100644 index 0000000000..10c273246b --- /dev/null +++ b/modules/misc/lua/callbacks.c @@ -0,0 +1,277 @@ +/***************************************************************************** + * callbacks.c: Generic lua<->vlc callbacks interface + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +typedef struct +{ + int i_index; + int i_type; + lua_State *L; +} vlclua_callback_t; + +static int vlclua_callback( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data; + lua_State *L = p_callback->L; + + /* */ + lua_getglobal( L, "vlc" ); + /* vlc */ + lua_getfield( L, -1, "callbacks" ); + /* vlc callbacks */ + lua_remove( L, -2 ); + /* callbacks */ + lua_pushinteger( L, p_callback->i_index ); + /* callbacks index */ + lua_gettable( L, -2 ); + /* callbacks callbacks[index] */ + lua_remove( L, -2 ); + /* callbacks[index] */ + lua_getfield( L, -1, "callback" ); + /* callbacks[index] callback */ + lua_pushstring( L, psz_var ); + /* callbacks[index] callback var */ + vlclua_pushvalue( L, p_callback->i_type, oldval ); + /* callbacks[index] callback var oldval */ + vlclua_pushvalue( L, p_callback->i_type, newval ); + /* callbacks[index] callback var oldval newval */ + lua_getfield( L, -5, "data" ); + /* callbacks[index] callback var oldval newval data */ + lua_remove( L, -6 ); + /* callback var oldval newval data */ + + if( lua_pcall( L, 4, 0, 0 ) ) + { + /* errormessage */ + const char *psz_err = lua_tostring( L, -1 ); + msg_Err( p_this, "Error while runing lua interface callback: %s", + psz_err ); + /* empty the stack (should only contain the error message) */ + lua_settop( L, 0 ); + return VLC_EGENERIC; + } + + /* empty the stack (should already be empty) */ + lua_settop( L, 0 ); + + return VLC_SUCCESS; +} + +int vlclua_add_callback( lua_State *L ) +{ + vlclua_callback_t *p_callback; + static int i_index = 0; + vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 ); + const char *psz_var = luaL_checkstring( L, 2 ); + lua_settop( L, 4 ); /* makes sure that optional data arg is set */ + if( !lua_isfunction( L, 3 ) ) + return vlclua_error( L ); + i_index++; + + p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) ); + if( !p_callback ) + return vlclua_error( L ); + + /* obj var func data */ + lua_getglobal( L, "vlc" ); + /* obj var func data vlc */ + lua_getfield( L, -1, "callbacks" ); + if( lua_isnil( L, -1 ) ) + { + lua_pop( L, 1 ); + lua_newtable( L ); + lua_setfield( L, -2, "callbacks" ); + lua_getfield( L, -1, "callbacks" ); + } + /* obj var func data vlc callbacks */ + lua_remove( L, -2 ); + /* obj var func data callbacks */ + lua_pushinteger( L, i_index ); + /* obj var func data callbacks index */ + lua_insert( L, -4 ); + /* obj var index func data callbacks */ + lua_insert( L, -4 ); + /* obj var callbacks index func data */ + lua_createtable( L, 0, 0 ); + /* obj var callbacks index func data cbtable */ + lua_insert( L, -2 ); + /* obj var callbacks index func cbtable data */ + lua_setfield( L, -2, "data" ); + /* obj var callbacks index func cbtable */ + lua_insert( L, -2 ); + /* obj var callbacks index cbtable func */ + lua_setfield( L, -2, "callback" ); + /* obj var callbacks index cbtable */ + lua_pushlightuserdata( L, p_obj ); /* will be needed in vlclua_del_callback */ + /* obj var callbacks index cbtable p_obj */ + lua_setfield( L, -2, "private1" ); + /* obj var callbacks index cbtable */ + lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */ + /* obj var callbacks index cbtable var */ + lua_setfield( L, -2, "private2" ); + /* obj var callbacks index cbtable */ + lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */ + /* obj var callbacks index cbtable p_callback */ + lua_setfield( L, -2, "private3" ); + /* obj var callbacks index cbtable */ + lua_settable( L, -3 ); + /* obj var callbacks */ + lua_pop( L, 3 ); + /* */ + + /* Do not move this before the lua specific code (it somehow changes + * the function in the stack to nil) */ + p_callback->i_index = i_index; + p_callback->i_type = var_Type( p_obj, psz_var ); + p_callback->L = lua_newthread( L ); + + var_AddCallback( p_obj, psz_var, vlclua_callback, p_callback ); + return 0; +} + +int vlclua_del_callback( lua_State *L ) +{ + vlclua_callback_t *p_callback; + vlc_bool_t b_found = VLC_FALSE; + vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 ); + const char *psz_var = luaL_checkstring( L, 2 ); + lua_settop( L, 4 ); /* makes sure that optional data arg is set */ + if( !lua_isfunction( L, 3 ) ) + return vlclua_error( L ); + + /* obj var func data */ + lua_getglobal( L, "vlc" ); + /* obj var func data vlc */ + lua_getfield( L, -1, "callbacks" ); + if( lua_isnil( L, -1 ) ) + return luaL_error( L, "Couldn't find matching callback." ); + /* obj var func data vlc callbacks */ + lua_remove( L, -2 ); + /* obj var func data callbacks */ + lua_pushnil( L ); + /* obj var func data callbacks index */ + while( lua_next( L, -2 ) ) + { + /* obj var func data callbacks index value */ + if( lua_isnumber( L, -2 ) ) + { + lua_getfield( L, -1, "private2" ); + /* obj var func data callbacks index value private2 */ + if( lua_equal( L, 2, -1 ) ) /* var name is equal */ + { + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + lua_getfield( L, -1, "callback" ); + /* obj var func data callbacks index value callback */ + if( lua_equal( L, 3, -1 ) ) /* callback function is equal */ + { + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + lua_getfield( L, -1, "data" ); /* callback data is equal */ + /* obj var func data callbacks index value data */ + if( lua_equal( L, 4, -1 ) ) + { + vlc_object_t *p_obj2; + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + lua_getfield( L, -1, "private1" ); + /* obj var func data callbacks index value private1 */ + p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 ); + if( p_obj2 == p_obj ) /* object is equal */ + { + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + lua_getfield( L, -1, "private3" ); + /* obj var func data callbacks index value private3 */ + p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 ); + lua_pop( L, 2 ); + /* obj var func data callbacks index */ + b_found = VLC_TRUE; + break; + } + else + { + /* obj var func data callbacks index value private1 */ + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + } + } + else + { + /* obj var func data callbacks index value data */ + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + } + } + else + { + /* obj var func data callbacks index value callback */ + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + } + } + else + { + /* obj var func data callbacks index value private2 */ + lua_pop( L, 1 ); + /* obj var func data callbacks index value */ + } + } + /* obj var func data callbacks index value */ + lua_pop( L, 1 ); + /* obj var func data callbacks index */ + } + if( b_found == VLC_FALSE ) + /* obj var func data callbacks */ + return luaL_error( L, "Couldn't find matching callback." ); + /* else */ + /* obj var func data callbacks index*/ + + var_DelCallback( p_obj, psz_var, vlclua_callback, p_callback ); + free( p_callback ); + + /* obj var func data callbacks index */ + lua_pushnil( L ); + /* obj var func data callbacks index nil */ + lua_settable( L, -3 ); /* delete the callback table entry */ + /* obj var func data callbacks */ + lua_pop( L, 5 ); + /* */ + return 0; +} diff --git a/modules/misc/lua/configuration.c b/modules/misc/lua/configuration.c new file mode 100644 index 0000000000..9286a6c700 --- /dev/null +++ b/modules/misc/lua/configuration.c @@ -0,0 +1,106 @@ +/***************************************************************************** + * configuration.c: Generic lua<->vlc config inteface + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +/***************************************************************************** + * Config handling + *****************************************************************************/ +int vlclua_config_get( lua_State *L ) +{ + vlc_object_t * p_this = vlclua_get_this( L ); + const char *psz_name; + psz_name = luaL_checkstring( L, 1 ); + switch( config_GetType( p_this, psz_name ) ) + { + case VLC_VAR_MODULE: + case VLC_VAR_STRING: + case VLC_VAR_FILE: + case VLC_VAR_DIRECTORY: + lua_pushstring( L, config_GetPsz( p_this, psz_name ) ); + break; + + case VLC_VAR_INTEGER: + lua_pushinteger( L, config_GetInt( p_this, psz_name ) ); + break; + + case VLC_VAR_BOOL: + lua_pushboolean( L, config_GetInt( p_this, psz_name ) ); + break; + + case VLC_VAR_FLOAT: + lua_pushnumber( L, config_GetFloat( p_this, psz_name ) ); + break; + + default: + return vlclua_error( L ); + + } + return 1; +} + +int vlclua_config_set( lua_State *L ) +{ + vlc_object_t *p_this = vlclua_get_this( L ); + const char *psz_name; + psz_name = luaL_checkstring( L, 1 ); + switch( config_GetType( p_this, psz_name ) ) + { + case VLC_VAR_MODULE: + case VLC_VAR_STRING: + case VLC_VAR_FILE: + case VLC_VAR_DIRECTORY: + config_PutPsz( p_this, psz_name, luaL_checkstring( L, 2 ) ); + break; + + case VLC_VAR_INTEGER: + config_PutInt( p_this, psz_name, luaL_checkint( L, 2 ) ); + break; + + case VLC_VAR_BOOL: + config_PutInt( p_this, psz_name, luaL_checkboolean( L, 2 ) ); + break; + + case VLC_VAR_FLOAT: + config_PutFloat( p_this, psz_name, + luaL_checknumber( L, 2 ) ); + break; + + default: + return vlclua_error( L ); + } + return 0; +} diff --git a/modules/misc/lua/intf.c b/modules/misc/lua/intf.c new file mode 100644 index 0000000000..01d36a9e95 --- /dev/null +++ b/modules/misc/lua/intf.c @@ -0,0 +1,897 @@ +/***************************************************************************** + * intf.c: Generic lua inteface functions + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +struct intf_sys_t +{ + char *psz_filename; + lua_State *L; +}; + +/***************************************************************************** + * Internal lua<->vlc utils + *****************************************************************************/ +static inline playlist_t *vlclua_get_playlist_internal( lua_State *L ) +{ + vlc_object_t *p_this = vlclua_get_this( L ); + return pl_Yield( p_this ); +} + +static input_thread_t * vlclua_get_input_internal( lua_State *L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + input_thread_t *p_input = p_playlist->p_input; + if( p_input ) vlc_object_yield( p_input ); + vlc_object_release( p_playlist ); + return p_input; +} + +/* FIXME: This is high level logic. Should be implemented in lua */ +#define vlclua_var_toggle_or_set(a,b,c) \ + __vlclua_var_toggle_or_set(a,VLC_OBJECT(b),c) +static int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj, + const char *psz_name ) +{ + vlc_bool_t b_bool; + if( lua_gettop( L ) > 1 ) return vlclua_error( L ); + + if( lua_gettop( L ) == 0 ) + b_bool = !var_GetBool( p_obj, psz_name ); + else /* lua_gettop( L ) == 1 */ + { + b_bool = luaL_checkboolean( L, -1 )?VLC_TRUE:VLC_FALSE; + lua_pop( L, 1 ); + } + + if( b_bool != var_GetBool( p_obj, psz_name ) ) + var_SetBool( p_obj, psz_name, b_bool ); + + lua_pushboolean( L, b_bool ); + return 1; +} + +/***************************************************************************** + * Libvlc TODO: move to vlc.c + *****************************************************************************/ +static int vlclua_get_libvlc( lua_State *L ) +{ + vlclua_push_vlc_object( L, vlclua_get_this( L )->p_libvlc, + NULL ); + return 1; +} + +/***************************************************************************** + * Input handling + *****************************************************************************/ +static int vlclua_get_input( lua_State *L ) +{ + input_thread_t *p_input = vlclua_get_input_internal( L ); + if( p_input ) + { + vlclua_push_vlc_object( L, p_input, vlclua_gc_release ); + } + else lua_pushnil( L ); + return 1; +} + +static int vlclua_input_info( lua_State *L ) +{ + input_thread_t * p_input = vlclua_get_input_internal( L ); + int i_cat; + int i; + if( !p_input ) return vlclua_error( L ); + vlc_mutex_lock( &input_GetItem(p_input)->lock ); + i_cat = input_GetItem(p_input)->i_categories; + lua_createtable( L, 0, i_cat ); + for( i = 0; i < i_cat; i++ ) + { + info_category_t *p_category = input_GetItem(p_input)->pp_categories[i]; + int i_infos = p_category->i_infos; + int j; + lua_pushstring( L, p_category->psz_name ); + lua_createtable( L, 0, i_infos ); + for( j = 0; j < i_infos; j++ ) + { + info_t *p_info = p_category->pp_infos[j]; + lua_pushstring( L, p_info->psz_name ); + lua_pushstring( L, p_info->psz_value ); + lua_settable( L, -3 ); + } + lua_settable( L, -3 ); + } + vlc_object_release( p_input ); + return 1; +} + +static int vlclua_is_playing( lua_State *L ) +{ + input_thread_t * p_input = vlclua_get_input_internal( L ); + lua_pushboolean( L, !!p_input ); + return 1; +} + +static int vlclua_get_title( lua_State *L ) +{ + input_thread_t *p_input = vlclua_get_input_internal( L ); + if( !p_input ) + lua_pushnil( L ); + else + { + lua_pushstring( L, input_GetItem(p_input)->psz_name ); + vlc_object_release( p_input ); + } + return 1; +} + +/***************************************************************************** + * Vout control + *****************************************************************************/ +static int vlclua_fullscreen( lua_State *L ) +{ + vout_thread_t *p_vout; + int i_ret; + + input_thread_t * p_input = vlclua_get_input_internal( L ); + if( !p_input ) return vlclua_error( L ); + + p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD ); + if( !p_vout ) return vlclua_error( L ); + + i_ret = vlclua_var_toggle_or_set( L, p_vout, "fullscreen" ); + vlc_object_release( p_vout ); + vlc_object_release( p_input ); + return i_ret; +} + +static int vlc_osd_icon_from_string( const char *psz_name ) +{ + static const struct + { + int i_icon; + const char *psz_name; + } pp_icons[] = + { { OSD_PAUSE_ICON, "pause" }, + { OSD_PLAY_ICON, "play" }, + { OSD_SPEAKER_ICON, "speaker" }, + { OSD_MUTE_ICON, "mute" }, + { 0, NULL } }; + int i; + for( i = 0; pp_icons[i].psz_name; i++ ) + { + if( !strcmp( psz_name, pp_icons[i].psz_name ) ) + return pp_icons[i].i_icon; + } + return 0; +} + +static int vlclua_osd_icon( lua_State *L ) +{ + const char *psz_icon = luaL_checkstring( L, 1 ); + int i_icon = vlc_osd_icon_from_string( psz_icon ); + int i_chan = luaL_optint( L, 2, DEFAULT_CHAN ); + if( !i_icon ) + return luaL_error( L, "\"%s\" is not a valid osd icon.", psz_icon ); + else + { + vlc_object_t *p_this = vlclua_get_this( L ); + vout_OSDIcon( p_this, i_chan, i_icon ); + return 0; + } +} + +static int vlclua_osd_message( lua_State *L ) +{ + const char *psz_message = luaL_checkstring( L, 1 ); + int i_chan = luaL_optint( L, 2, DEFAULT_CHAN ); + vlc_object_t *p_this = vlclua_get_this( L ); + vout_OSDMessage( p_this, i_chan, psz_message ); + return 0; +} + +static int vlc_osd_slider_type_from_string( const char *psz_name ) +{ + static const struct + { + int i_type; + const char *psz_name; + } pp_types[] = + { { OSD_HOR_SLIDER, "horizontal" }, + { OSD_VERT_SLIDER, "vertical" }, + { 0, NULL } }; + int i; + for( i = 0; pp_types[i].psz_name; i++ ) + { + if( !strcmp( psz_name, pp_types[i].psz_name ) ) + return pp_types[i].i_type; + } + return 0; +} + +static int vlclua_osd_slider( lua_State *L ) +{ + int i_position = luaL_checkint( L, 1 ); + const char *psz_type = luaL_checkstring( L, 2 ); + int i_type = vlc_osd_slider_type_from_string( psz_type ); + int i_chan = luaL_optint( L, 3, DEFAULT_CHAN ); + if( !i_type ) + return luaL_error( L, "\"%s\" is not a valid slider type.", + psz_type ); + else + { + vlc_object_t *p_this = vlclua_get_this( L ); + vout_OSDSlider( p_this, i_chan, i_position, i_type ); + return 0; + } +} + +static int vlclua_spu_channel_register( lua_State *L ) +{ + int i_chan; + vlc_object_t *p_this = vlclua_get_this( L ); + vout_thread_t *p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, + FIND_ANYWHERE ); + if( !p_vout ) + return luaL_error( L, "Unable to find vout." ); + + spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER, &i_chan ); + vlc_object_release( p_vout ); + lua_pushinteger( L, i_chan ); + return 1; +} + +static int vlclua_spu_channel_clear( lua_State *L ) +{ + int i_chan = luaL_checkint( L, 1 ); + vlc_object_t *p_this = vlclua_get_this( L ); + vout_thread_t *p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, + FIND_ANYWHERE ); + if( !p_vout ) + return luaL_error( L, "Unable to find vout." ); + + spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, i_chan ); + vlc_object_release( p_vout ); + return 0; +} + +/***************************************************************************** + * Playlist control + *****************************************************************************/ +static int vlclua_get_playlist( lua_State *L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + if( p_playlist ) + { + vlclua_push_vlc_object( L, p_playlist, vlclua_gc_release ); + } + else lua_pushnil( L ); + return 1; +} + +static int vlclua_playlist_prev( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + playlist_Prev( p_playlist ); + vlc_object_release( p_playlist ); + return 0; +} + +static int vlclua_playlist_next( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + playlist_Next( p_playlist ); + vlc_object_release( p_playlist ); + return 0; +} + +static int vlclua_playlist_play( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + playlist_Play( p_playlist ); + vlc_object_release( p_playlist ); + return 0; +} + +static int vlclua_playlist_stop( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + playlist_Stop( p_playlist ); + vlc_object_release( p_playlist ); + return 0; +} + +static int vlclua_playlist_clear( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + playlist_Stop( p_playlist ); /* Isn't this already implied by Clear? */ + playlist_Clear( p_playlist, VLC_FALSE ); + vlc_object_release( p_playlist ); + return 0; +} + +static int vlclua_playlist_repeat( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "repeat" ); + vlc_object_release( p_playlist ); + return i_ret; +} + +static int vlclua_playlist_loop( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "loop" ); + vlc_object_release( p_playlist ); + return i_ret; +} + +static int vlclua_playlist_random( lua_State * L ) +{ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "random" ); + vlc_object_release( p_playlist ); + return i_ret; +} + +static int vlclua_playlist_goto( lua_State * L ) +{ + /* XXX: logic copied from rc.c ... i'm not sure that it's ok as it + * implies knowledge of the playlist internals. */ + playlist_t *p_playlist; + int i_size; + playlist_item_t *p_item, *p_parent; + + int i_pos; + if( lua_gettop( L ) != 1 ) return vlclua_error( L ); + i_pos = luaL_checkint( L, -1 ); + lua_pop( L, 1 ); + if( i_pos <= 0 ) return 0; + + p_playlist = vlclua_get_playlist_internal( L ); + /* The playlist stores 2 times the same item: onelevel & category */ + i_size = p_playlist->items.i_size / 2; + + if( i_pos > i_size ) + { + vlc_object_release( p_playlist ); + return 0; + } + + p_item = p_parent = p_playlist->items.p_elems[i_pos*2-1]; + while( p_parent->p_parent ) + p_parent = p_parent->p_parent; + playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE, + p_parent, p_item ); + + vlc_object_release( p_playlist ); + lua_pushboolean( L, 1 ); + return 1; +} + +static int vlclua_playlist_add( lua_State *L ) +{ + int i_count; + vlc_object_t *p_this = vlclua_get_this( L ); + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + i_count = vlclua_playlist_add_internal( p_this, L, p_playlist, + NULL, VLC_TRUE ); + vlc_object_release( p_playlist ); + lua_pushinteger( L, i_count ); + return 1; +} + +static int vlclua_playlist_enqueue( lua_State *L ) +{ + int i_count; + vlc_object_t *p_this = vlclua_get_this( L ); + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + i_count = vlclua_playlist_add_internal( p_this, L, p_playlist, + NULL, VLC_FALSE ); + vlc_object_release( p_playlist ); + lua_pushinteger( L, i_count ); + return 1; +} + +static int vlclua_playlist_get( lua_State *L ) +{ + /* TODO: make it possible to get the tree playlist */ + playlist_t *p_playlist = vlclua_get_playlist_internal( L ); + playlist_item_t *p_root; + int i; + if( lua_isboolean( L, 1 ) && lua_toboolean( L, 1 ) ) + p_root = p_playlist->p_ml_onelevel; /* media library */ + else + p_root = p_playlist->p_local_onelevel; /* local/normal playlist */ + lua_createtable( L, p_root->i_children, 0 ); + for( i = 0; i < p_root->i_children; i++ ) + { + playlist_item_t *p_item = p_root->pp_children[i]; + input_item_t *p_input = p_item->p_input; + lua_pushinteger( L, i+1 ); + lua_newtable( L ); + lua_pushstring( L, p_input->psz_name ); + lua_setfield( L, -2, "name" ); + lua_pushstring( L, p_input->psz_uri ); + lua_setfield( L, -2, "path" ); + lua_pushnumber( L, ((double)p_input->i_duration)*1e-6 ); + lua_setfield( L, -2, "duration" ); + lua_pushinteger( L, p_input->i_nb_played ); + lua_setfield( L, -2, "nb_played" ); + /* TODO: add (optional) info categories, meta, options, es */ + lua_settable( L, -3 ); + } + vlc_object_release( p_playlist ); + return 1; +} + +static int vlclua_playlist_sort( lua_State *L ) +{ + /* allow setting the different sort keys */ + return 0; +} + +/* FIXME: split this in 3 different functions? */ +static int vlclua_playlist_status( lua_State *L ) +{ + intf_thread_t *p_intf = (intf_thread_t *)vlclua_get_this( L ); + playlist_t *p_playlist = pl_Yield( p_intf ); + int i_count = 0; + lua_settop( L, 0 ); + if( p_playlist->p_input ) + { + char *psz_uri = + input_item_GetURI( input_GetItem( p_playlist->p_input ) ); + lua_pushstring( L, psz_uri ); + free( psz_uri ); + lua_pushnumber( L, config_GetInt( p_intf, "volume" ) ); + vlc_mutex_lock( &p_playlist->object_lock ); + switch( p_playlist->status.i_status ) + { + case PLAYLIST_STOPPED: + lua_pushstring( L, "stopped" ); + break; + case PLAYLIST_RUNNING: + lua_pushstring( L, "running" ); + break; + case PLAYLIST_PAUSED: + lua_pushstring( L, "paused" ); + break; + default: + lua_pushstring( L, "unknown" ); + break; + } + vlc_mutex_unlock( &p_playlist->object_lock ); + i_count += 3; + } + vlc_object_release( p_playlist ); + return i_count; +} + + +static int vlclua_lock_and_wait( lua_State *L ) +{ + vlc_object_t *p_this = vlclua_get_this( L ); + int b_quit = vlc_object_lock_and_wait( p_this ); + lua_pushboolean( L, b_quit ); + return 1; +} + +static int vlclua_signal( lua_State *L ) +{ + vlc_object_t *p_this = vlclua_get_this( L ); + vlc_object_signal( p_this ); + return 0; +} + +static int vlclua_mdate( lua_State *L ) +{ + lua_pushnumber( L, mdate() ); + return 1; +} + +static int vlclua_intf_should_die( lua_State *L ) +{ + intf_thread_t *p_intf = (intf_thread_t*)vlclua_get_this( L ); + lua_pushboolean( L, intf_ShouldDie( p_intf ) ); + return 1; +} + +static luaL_Reg p_reg[] = +{ + { "input_info", vlclua_input_info }, + { "is_playing", vlclua_is_playing }, + { "get_title", vlclua_get_title }, + + { "fullscreen", vlclua_fullscreen }, + + { "mdate", vlclua_mdate }, + + { "module_command", vlclua_module_command }, + { "libvlc_command", vlclua_libvlc_command }, + + { "decode_uri", vlclua_decode_uri }, + { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars }, + + { "lock_and_wait", vlclua_lock_and_wait }, + { "signal", vlclua_signal }, + + { "version", vlclua_version }, + { "should_die", vlclua_intf_should_die }, + { "quit", vlclua_quit }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_object[] = +{ + { "input", vlclua_get_input }, /* This is fast */ + { "playlist", vlclua_get_playlist }, /* This is fast */ + { "libvlc", vlclua_get_libvlc }, /* This is fast */ + + { "find", vlclua_object_find }, /* This is slow */ + { "find_name", vlclua_object_find_name }, /* This is slow */ + + { NULL, NULL } +}; + +static luaL_Reg p_reg_var[] = +{ + { "get", vlclua_var_get }, + { "get_list", vlclua_var_get_list }, + { "set", vlclua_var_set }, + { "add_callback", vlclua_add_callback }, + { "del_callback", vlclua_del_callback }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_config[] = +{ + { "get", vlclua_config_get }, + { "set", vlclua_config_set }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_msg[] = +{ + { "dbg", vlclua_msg_dbg }, + { "warn", vlclua_msg_warn }, + { "err", vlclua_msg_err }, + { "info", vlclua_msg_info }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_playlist[] = +{ + { "prev", vlclua_playlist_prev }, + { "next", vlclua_playlist_next }, + { "play", vlclua_playlist_play }, + { "stop", vlclua_playlist_stop }, + { "clear", vlclua_playlist_clear }, + { "repeat_", vlclua_playlist_repeat }, + { "loop", vlclua_playlist_loop }, + { "random", vlclua_playlist_random }, + { "goto", vlclua_playlist_goto }, + { "status", vlclua_playlist_status }, + { "add", vlclua_playlist_add }, + { "enqueue", vlclua_playlist_enqueue }, + { "get", vlclua_playlist_get }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_volume[] = +{ + { "get", vlclua_volume_get }, + { "set", vlclua_volume_set }, + { "up", vlclua_volume_up }, + { "down", vlclua_volume_down }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_osd[] = +{ + { "icon", vlclua_osd_icon }, + { "message", vlclua_osd_message }, + { "slider", vlclua_osd_slider }, + { "channel_register", vlclua_spu_channel_register }, + { "channel_clear", vlclua_spu_channel_clear }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_net[] = +{ + { "url_parse", vlclua_url_parse }, + { "listen_tcp", vlclua_net_listen_tcp }, + { "listen_close", vlclua_net_listen_close }, + { "accept", vlclua_net_accept }, + { "close", vlclua_net_close }, + { "send", vlclua_net_send }, + { "recv", vlclua_net_recv }, + { "select", vlclua_net_select }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_fd[] = +{ +/* { "open", vlclua_fd_open },*/ + { "read", vlclua_fd_read }, + { "write", vlclua_fd_write }, + + { "new_fd_set", vlclua_fd_set_new }, + { "fd_clr", vlclua_fd_clr }, + { "fd_isset", vlclua_fd_isset }, + { "fd_set", vlclua_fd_set }, + { "fd_zero", vlclua_fd_zero }, + + { NULL, NULL } +}; + +static luaL_Reg p_reg_vlm[] = +{ + { "new", vlclua_vlm_new }, + { "delete", vlclua_vlm_delete }, + { "execute_command", vlclua_vlm_execute_command }, + + { NULL, NULL } +}; + + +static void Run( intf_thread_t *p_intf ); + +static char *FindFile( intf_thread_t *p_intf, const char *psz_name ) +{ + char *ppsz_dir_list[] = { NULL, NULL, NULL, NULL }; + char **ppsz_dir; + vlclua_dir_list( VLC_OBJECT(p_intf), "luaintf", ppsz_dir_list ); + for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ ) + { + char *psz_filename; + FILE *fp; + if( asprintf( &psz_filename, "%s"DIR_SEP"%s.lua", *ppsz_dir, + psz_name ) < 0 ) + { + return NULL; + } + fp = fopen( psz_filename, "r" ); + if( fp ) + { + fclose( fp ); + return psz_filename; + } + free( psz_filename ); + } + return NULL; +} + +static inline void luaL_register_submodule( lua_State *L, const char *psz_name, + const luaL_Reg *l ) +{ + lua_newtable( L ); + luaL_register( L, NULL, l ); + lua_setfield( L, -2, psz_name ); +} + +static struct +{ + const char *psz_shortcut; + const char *psz_name; +} pp_shortcuts[] = { + { "luarc", "rc" }, + /* { "rc", "rc" }, */ + { "luahotkeys", "hotkeys" }, + /* { "hotkeys", "hotkeys" }, */ + { "luatelnet", "telnet" }, + /* { "telnet", "telnet" }, */ + { NULL, NULL } }; + +static vlc_bool_t WordInList( const char *psz_list, const char *psz_word ) +{ + const char *psz_str = strstr( psz_list, psz_word ); + int i_len = strlen( psz_word ); + while( psz_str ) + { + if( (psz_str == psz_list || *(psz_str-1) == ',' ) + /* it doesn't start in middle of a word */ + /* it doest end in middle of a word */ + && ( psz_str[i_len] == '\0' || psz_str[i_len] == ',' ) ) + return VLC_TRUE; + psz_str = strstr( psz_str, psz_word ); + } + return VLC_FALSE; +} + +static const char *GetModuleName( intf_thread_t *p_intf ) +{ + int i; + const char *psz_intf; + if( *p_intf->psz_intf == '$' ) + psz_intf = var_GetString( p_intf, p_intf->psz_intf+1 ); + else + psz_intf = p_intf->psz_intf; + for( i = 0; pp_shortcuts[i].psz_name; i++ ) + { + if( WordInList( psz_intf, pp_shortcuts[i].psz_shortcut ) ) + return pp_shortcuts[i].psz_name; + } + + return config_GetPsz( p_intf, "lua-intf" ); +} + +int E_(Open_LuaIntf)( vlc_object_t *p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t*)p_this; + intf_sys_t *p_sys; + lua_State *L; + + const char *psz_name = GetModuleName( p_intf ); + const char *psz_config; + vlc_bool_t b_config_set = VLC_FALSE; + if( !psz_name ) psz_name = "dummy"; + + p_intf->p_sys = (intf_sys_t*)malloc( sizeof(intf_sys_t*) ); + if( !p_intf->p_sys ) + { + return VLC_ENOMEM; + } + p_sys = p_intf->p_sys; + p_sys->psz_filename = FindFile( p_intf, psz_name ); + if( !p_sys->psz_filename ) + { + msg_Err( p_intf, "Couldn't find lua interface script \"%s\".", + psz_name ); + return VLC_EGENERIC; + } + msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename ); + + L = luaL_newstate(); + if( !L ) + { + msg_Err( p_intf, "Could not create new Lua State" ); + free( p_sys ); + return VLC_EGENERIC; + } + + luaL_openlibs( L ); /* FIXME: we don't want to have all the libs */ + + /* register our functions */ + luaL_register( L, "vlc", p_reg ); + /* store a pointer to p_intf */ + lua_pushlightuserdata( L, p_intf ); + lua_setfield( L, -2, "private" ); + /* register submodules */ + luaL_register_submodule( L, "object", p_reg_object ); + luaL_register_submodule( L, "var", p_reg_var ); + luaL_register_submodule( L, "config", p_reg_config ); + luaL_register_submodule( L, "msg", p_reg_msg ); + luaL_register_submodule( L, "playlist", p_reg_playlist ); + luaL_register_submodule( L, "volume", p_reg_volume ); + luaL_register_submodule( L, "osd", p_reg_osd ); + luaL_register_submodule( L, "net", p_reg_net ); + luaL_register_submodule( L, "fd", p_reg_fd ); + luaL_register_submodule( L, "vlm", p_reg_vlm ); + /* clean up */ + lua_pop( L, 1 ); + + /* */ + /* Setup the module search path */ + { + char *psz_command; + char *psz_char = strrchr(p_sys->psz_filename,DIR_SEP_CHAR); + *psz_char = '\0'; + /* FIXME: don't use luaL_dostring */ + if( asprintf( &psz_command, + "package.path = \"%s"DIR_SEP"modules"DIR_SEP"?.lua;\"..package.path", + p_sys->psz_filename ) < 0 ) + return VLC_EGENERIC; + *psz_char = DIR_SEP_CHAR; + if( luaL_dostring( L, psz_command ) ) + return VLC_EGENERIC; + } + /* */ + + psz_config = config_GetPsz( p_intf, "lua-config" ); + if( psz_config && *psz_config ) + { + char *psz_buffer; + if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 ) + { + printf("%s\n", psz_buffer); + if( luaL_dostring( L, psz_buffer ) == 1 ) + msg_Err( p_intf, "Error while parsing \"lua-config\"." ); + free( psz_buffer ); + lua_getglobal( L, "config" ); + if( lua_istable( L, -1 ) ) + { + lua_getfield( L, -1, psz_name ); + if( lua_istable( L, -1 ) ) + { + lua_setglobal( L, "config" ); + b_config_set = VLC_TRUE; + } + } + } + } + if( b_config_set == VLC_FALSE ) + { + lua_newtable( L ); + lua_setglobal( L, "config" ); + } + + p_sys->L = L; + + p_intf->pf_run = Run; + p_intf->psz_header = strdup( psz_name ); /* Do I need to clean that up myself in E_(Close_LuaIntf)? */ + + return VLC_SUCCESS; +} + +void E_(Close_LuaIntf)( vlc_object_t *p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t*)p_this; + + lua_close( p_intf->p_sys->L ); + free( p_intf->p_sys ); +} + +static void Run( intf_thread_t *p_intf ) +{ + lua_State *L = p_intf->p_sys->L; + + if( luaL_dofile( L, p_intf->p_sys->psz_filename ) ) + { + msg_Err( p_intf, "Error loading script %s: %s", + p_intf->p_sys->psz_filename, + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); + p_intf->b_die = VLC_TRUE; + return; + } + p_intf->b_die = VLC_TRUE; +} diff --git a/modules/misc/lua/luaplaylist.c b/modules/misc/lua/luaplaylist.c deleted file mode 100644 index dd883d6efd..0000000000 --- a/modules/misc/lua/luaplaylist.c +++ /dev/null @@ -1,414 +0,0 @@ -/***************************************************************************** - * luaplaylist.c : Lua playlist demux module - ***************************************************************************** - * Copyright (C) 2007 the VideoLAN team - * $Id$ - * - * Authors: Antoine Cellerier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include -#include -#include -#include -#include - -#include /* ENOMEM */ -#ifdef HAVE_SYS_STAT_H -# include -#endif - -#include "vlclua.h" - - -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Demux( demux_t *p_demux ); -static int Control( demux_t *p_demux, int i_query, va_list args ); - -/***************************************************************************** - * - *****************************************************************************/ -struct demux_sys_t -{ - lua_State *p_state; - char *psz_filename; -}; - -/***************************************************************************** - * - *****************************************************************************/ - -static int vlclua_demux_peek( lua_State *p_state ) -{ - demux_t *p_demux = (demux_t *)vlclua_get_this( p_state ); - int i = lua_gettop( p_state ); - int n; - byte_t *p_peek; - int i_peek; - if( !i ) return 0; - n = lua_tonumber( p_state, 1 ); - lua_pop( p_state, i ); - i_peek = stream_Peek( p_demux->s, &p_peek, n ); - lua_pushlstring( p_state, (const char *)p_peek, i_peek ); - return 1; -} - -static int vlclua_demux_read( lua_State *p_state ) -{ - demux_t *p_demux = (demux_t *)vlclua_get_this( p_state ); - int i = lua_gettop( p_state ); - int n; - byte_t *p_read; - int i_read; - if( !i ) return 0; - n = lua_tonumber( p_state, 1 ); - lua_pop( p_state, i ); - i_read = stream_Read( p_demux->s, &p_read, n ); - lua_pushlstring( p_state, (const char *)p_read, i_read ); - return 1; -} - -static int vlclua_demux_readline( lua_State *p_state ) -{ - demux_t *p_demux = (demux_t *)vlclua_get_this( p_state ); - char *psz_line = stream_ReadLine( p_demux->s ); - if( psz_line ) - { - lua_pushstring( p_state, psz_line ); - free( psz_line ); - } - else - { - lua_pushnil( p_state ); - } - return 1; -} - - -/* Functions to register */ -static luaL_Reg p_reg[] = -{ - { "peek", vlclua_demux_peek }, - { "decode_uri", vlclua_decode_uri }, - { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars }, - { "msg_dbg", vlclua_msg_dbg }, - { "msg_warn", vlclua_msg_warn }, - { "msg_err", vlclua_msg_err }, - { "msg_info", vlclua_msg_info }, - { NULL, NULL } -}; - -/* Functions to register for parse() function call only */ -static luaL_Reg p_reg_parse[] = -{ - { "read", vlclua_demux_read }, - { "readline", vlclua_demux_readline }, - { NULL, NULL } -}; - -/***************************************************************************** - * Called through lua_scripts_batch_execute to call 'probe' on - * the script pointed by psz_filename. - *****************************************************************************/ -static int probe_luascript( vlc_object_t *p_this, const char * psz_filename, - lua_State * p_state, void * user_data ) -{ - demux_t * p_demux = (demux_t *)p_this; - - p_demux->p_sys->psz_filename = strdup(psz_filename); - - /* Ugly hack to delete previous versions of the probe() and parse() - * functions. */ - lua_pushnil( p_state ); - lua_pushnil( p_state ); - lua_setglobal( p_state, "probe" ); - lua_setglobal( p_state, "parse" ); - - /* Load and run the script(s) */ - if( luaL_dofile( p_state, psz_filename ) ) - { - msg_Warn( p_demux, "Error loading script %s: %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); - return VLC_EGENERIC; - } - - lua_getglobal( p_state, "probe" ); - - if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) - { - msg_Warn( p_demux, "Error while runing script %s, " - "function probe() not found", psz_filename ); - lua_pop( p_state, 1 ); - return VLC_EGENERIC; - } - - if( lua_pcall( p_state, 0, 1, 0 ) ) - { - msg_Warn( p_demux, "Error while runing script %s, " - "function probe(): %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); - return VLC_EGENERIC; - } - - if( lua_gettop( p_state ) ) - { - int i_ret = VLC_EGENERIC; - if( lua_toboolean( p_state, 1 ) ) - { - msg_Dbg( p_demux, "Lua playlist script %s's " - "probe() function was successful", psz_filename ); - i_ret = VLC_SUCCESS; - } - lua_pop( p_state, 1 ); - - return i_ret; - } - return VLC_EGENERIC; -} - -/***************************************************************************** - * Import_LuaPlaylist: main import function - *****************************************************************************/ -int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) -{ - demux_t *p_demux = (demux_t *)p_this; - lua_State *p_state; - - p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) ); - if( !p_demux->p_sys ) - { - return VLC_ENOMEM; - } - - p_demux->p_sys->psz_filename = NULL; - - p_demux->pf_control = Control; - p_demux->pf_demux = Demux; - - /* Initialise Lua state structure */ - p_state = luaL_newstate(); - if( !p_state ) - { - msg_Err( p_demux, "Could not create new Lua State" ); - free( p_demux->p_sys ); - return VLC_EGENERIC; - } - p_demux->p_sys->p_state = p_state; - - /* Load Lua libraries */ - luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */ - - luaL_register( p_state, "vlc", p_reg ); - lua_pushlightuserdata( p_state, p_demux ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" ); - lua_pushstring( p_state, p_demux->psz_path ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "path" ); - lua_pushstring( p_state, p_demux->psz_access ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "access" ); - - lua_pop( p_state, 1 ); - - return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript, - p_state, NULL ); -} - - - -/***************************************************************************** - * Deactivate: frees unused data - *****************************************************************************/ -void E_(Close_LuaPlaylist)( vlc_object_t *p_this ) -{ - demux_t *p_demux = (demux_t *)p_this; - lua_close( p_demux->p_sys->p_state ); - free( p_demux->p_sys->psz_filename ); - free( p_demux->p_sys ); -} - -static inline void read_options( demux_t *p_demux, lua_State *p_state, - int o, int t, int *pi_options, - char ***pppsz_options ) -{ - lua_getfield( p_state, o, "options" ); - if( lua_istable( p_state, t ) ) - { - lua_pushnil( p_state ); - while( lua_next( p_state, t ) ) - { - if( lua_isstring( p_state, t+2 ) ) - { - char *psz_option = strdup( lua_tostring( p_state, t+2 ) ); - msg_Dbg( p_demux, "Option: %s", psz_option ); - INSERT_ELEM( *pppsz_options, *pi_options, *pi_options, - psz_option ); - } - else - { - msg_Warn( p_demux, "Option should be a string" ); - } - lua_pop( p_state, 1 ); /* pop option */ - } - } - lua_pop( p_state, 1 ); /* pop "options" */ -} - - -static int Demux( demux_t *p_demux ) -{ - input_item_t *p_input; - lua_State *p_state = p_demux->p_sys->p_state; - char *psz_filename = p_demux->p_sys->psz_filename; - int t; - - playlist_t *p_playlist = pl_Yield( p_demux ); - input_thread_t *p_input_thread = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT ); - input_item_t *p_current_input = input_GetItem( p_input_thread ); - - luaL_register( p_state, "vlc", p_reg_parse ); - - lua_getglobal( p_state, "parse" ); - - if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) - { - msg_Warn( p_demux, "Error while runing script %s, " - "function parse() not found", psz_filename ); - E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); - return VLC_EGENERIC; - } - - if( lua_pcall( p_state, 0, 1, 0 ) ) - { - msg_Warn( p_demux, "Error while runing script %s, " - "function parse(): %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); - return VLC_EGENERIC; - } - - /* Check that the Lua stack is big enough and grow it if needed. - * Should be ok since LUA_MINSTACK is 20 but we never know. */ - lua_checkstack( p_state, 8 ); - - if( ( t = lua_gettop( p_state ) ) ) - { - - if( lua_istable( p_state, t ) ) - { - lua_pushnil( p_state ); - while( lua_next( p_state, t ) ) - { - if( lua_istable( p_state, t+2 ) ) - { - lua_getfield( p_state, t+2, "path" ); - if( lua_isstring( p_state, t+3 ) ) - { - const char *psz_path = NULL; - const char *psz_name = NULL; - char **ppsz_options = NULL; - int i_options = 0; - mtime_t i_duration = -1; - - /* Read path and name */ - psz_path = lua_tostring( p_state, t+3 ); - msg_Dbg( p_demux, "Path: %s", psz_path ); - lua_getfield( p_state, t+2, "name" ); - if( lua_isstring( p_state, t+4 ) ) - { - psz_name = lua_tostring( p_state, t+4 ); - msg_Dbg( p_demux, "Name: %s", psz_name ); - } - else - { - psz_name = psz_path; - } - - /* Read duration */ - lua_getfield( p_state, t+2, "duration" ); - if( lua_isnumber( p_state, t+5 ) ) - { - i_duration = (mtime_t)lua_tointeger( p_state, t+5 ); - i_duration *= 1000000; - } - lua_pop( p_state, 1 ); /* pop "duration" */ - - /* Read options */ - read_options( p_demux, p_state, t+2, t+5, - &i_options, &ppsz_options ); - - /* Create input item */ - p_input = input_ItemNewExt( p_playlist, psz_path, - psz_name, i_options, - (const char **)ppsz_options, - i_duration ); - lua_pop( p_state, 1 ); /* pop "name" */ - - /* Read meta data */ - vlclua_read_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4, p_input ); - - /* Read custom meta data */ - vlclua_read_custom_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4, - p_input ); - - /* Append item to playlist */ - input_ItemAddSubItem( p_current_input, p_input ); - - while( i_options > 0 ) - free( ppsz_options[--i_options] ); - free( ppsz_options ); - } - else - { - msg_Warn( p_demux, - "Playlist item's path should be a string" ); - } - lua_pop( p_state, 1 ); /* pop "path" */ - } - else - { - msg_Warn( p_demux, "Playlist item should be a table" ); - } - lua_pop( p_state, 1 ); /* pop the value, keep the key for - * the next lua_next() call */ - } - } - else - { - msg_Warn( p_demux, "Script didn't return a table" ); - } - } - else - { - msg_Err( p_demux, "Script went completely foobar" ); - } - - vlc_object_release( p_input_thread ); - vlc_object_release( p_playlist ); - - return -1; /* Needed for correct operation of go back */ -} - -static int Control( demux_t *p_demux, int i_query, va_list args ) -{ - return VLC_EGENERIC; -} diff --git a/modules/misc/lua/luameta.c b/modules/misc/lua/meta.c similarity index 69% rename from modules/misc/lua/luameta.c rename to modules/misc/lua/meta.c index 4374ada350..9566c73c35 100644 --- a/modules/misc/lua/luameta.c +++ b/modules/misc/lua/meta.c @@ -1,5 +1,5 @@ /***************************************************************************** - * luameta.c: Get meta/artwork using lua scripts + * meta.c: Get meta/artwork using lua scripts ***************************************************************************** * Copyright (C) 2007 the VideoLAN team * $Id$ @@ -38,17 +38,18 @@ #include #include -#include "vlclua.h" +#include "vlc.h" /***************************************************************************** * Local prototypes *****************************************************************************/ static int fetch_meta( vlc_object_t *p_this, const char * psz_filename, - lua_State * p_state, void * user_data ); + lua_State * L, void * user_data ); static int fetch_art( vlc_object_t *p_this, const char * psz_filename, - lua_State * p_state, void * user_data ); -static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ); + lua_State * L, void * user_data ); +static lua_State *vlclua_meta_init( vlc_object_t *p_this, + input_item_t * p_item ); /***************************************************************************** @@ -57,6 +58,20 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item /* Functions to register */ static luaL_Reg p_reg[] = { + /* TODO: make an object out of the stream stuff, so we can do something + * like: + * + * s = vlc.stream_new( "http://www.videolan.org" ) + * page = s:read( 2^16 ) + * s:delete() + * + * instead of (which could be problematic since we don't check if + * s is really a stream object): + * + * s = vlc.stream_new( "http://www.videolan.org" ) + * page = vlc.stream_read( s, 2^16 ) + * vlc.stream_delete( s ) + */ { "stream_new", vlclua_stream_new }, { "stream_read", vlclua_stream_read }, { "stream_readline", vlclua_stream_readline }, @@ -75,8 +90,8 @@ static luaL_Reg p_reg[] = *****************************************************************************/ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item ) { - lua_State * p_state = luaL_newstate(); - if( !p_state ) + lua_State * L = luaL_newstate(); + if( !L ) { msg_Err( p_this, "Could not create new Lua State" ); return NULL; @@ -84,40 +99,40 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item char *psz_meta; /* Load Lua libraries */ - luaL_openlibs( p_state ); /* XXX: Don't open all the libs? */ + luaL_openlibs( L ); /* XXX: Don't open all the libs? */ - luaL_register( p_state, "vlc", p_reg ); + luaL_register( L, "vlc", p_reg ); - lua_pushlightuserdata( p_state, p_this ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" ); + lua_pushlightuserdata( L, p_this ); + lua_setfield( L, -2, "private" ); psz_meta = input_item_GetName( p_item ); - lua_pushstring( p_state, psz_meta ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "name" ); + lua_pushstring( L, psz_meta ); + lua_setfield( L, -2, "name" ); free( psz_meta ); psz_meta = input_item_GetArtist( p_item ); - lua_pushstring( p_state, psz_meta ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "artist" ); + lua_pushstring( L, psz_meta ); + lua_setfield( L, -2, "artist" ); free( psz_meta ); psz_meta = input_item_GetTitle( p_item ) ; - lua_pushstring( p_state, psz_meta ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "title" ); + lua_pushstring( L, psz_meta ); + lua_setfield( L, -2, "title" ); free( psz_meta ); psz_meta = input_item_GetAlbum( p_item ); - lua_pushstring( p_state, psz_meta ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "album" ); + lua_pushstring( L, psz_meta ); + lua_setfield( L, -2, "album" ); free( psz_meta ); psz_meta = input_item_GetArtURL( p_item ); - lua_pushstring( p_state, psz_meta ); - lua_setfield( p_state, lua_gettop( p_state ) - 1, "arturl" ); + lua_pushstring( L, psz_meta ); + lua_setfield( L, -2, "arturl" ); free( psz_meta ); /* XXX: all should be passed ( could use macro ) */ - return p_state; + return L; } /***************************************************************************** @@ -125,7 +140,7 @@ static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item * pointed by psz_filename. *****************************************************************************/ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, - lua_State * p_state, void * user_data ) + lua_State * L, void * user_data ) { int i_ret = VLC_EGENERIC; input_item_t * p_input = user_data; @@ -133,44 +148,44 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, /* Ugly hack to delete previous versions of the fetchart() * functions. */ - lua_pushnil( p_state ); - lua_setglobal( p_state, "fetch_art" ); + lua_pushnil( L ); + lua_setglobal( L, "fetch_art" ); /* Load and run the script(s) */ - if( luaL_dofile( p_state, psz_filename ) ) + if( luaL_dofile( L, psz_filename ) ) { msg_Warn( p_this, "Error loading script %s: %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); return VLC_EGENERIC; } - lua_getglobal( p_state, "fetch_art" ); + lua_getglobal( L, "fetch_art" ); - if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) + if( !lua_isfunction( L, lua_gettop( L ) ) ) { msg_Warn( p_this, "Error while runing script %s, " "function fetch_art() not found", psz_filename ); - lua_pop( p_state, 1 ); + lua_pop( L, 1 ); return VLC_EGENERIC; } - if( lua_pcall( p_state, 0, 1, 0 ) ) + if( lua_pcall( L, 0, 1, 0 ) ) { msg_Warn( p_this, "Error while runing script %s, " "function fetch_art(): %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); return VLC_EGENERIC; } - if((s = lua_gettop( p_state ))) + if((s = lua_gettop( L ))) { const char * psz_value; - if( lua_isstring( p_state, s ) ) + if( lua_isstring( L, s ) ) { - psz_value = lua_tostring( p_state, s ); + psz_value = lua_tostring( L, s ); if( psz_value && *psz_value != 0 ) { lua_Dbg( p_this, "setting arturl: %s", psz_value ); @@ -178,7 +193,7 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, i_ret = VLC_SUCCESS; } } - else if( !lua_isnil( p_state, s ) ) + else if( !lua_isnil( L, s ) ) { msg_Err( p_this, "Lua art fetcher script %s: " "didn't return a string", psz_filename ); @@ -197,56 +212,55 @@ static int fetch_art( vlc_object_t *p_this, const char * psz_filename, * pointed by psz_filename. *****************************************************************************/ static int fetch_meta( vlc_object_t *p_this, const char * psz_filename, - lua_State * p_state, void * user_data ) + lua_State * L, void * user_data ) { input_item_t * p_input = user_data; - int t; - /* Ugly hack to delete previous versions of the fetchmeta() - * functions. */ - lua_pushnil( p_state ); - lua_setglobal( p_state, "fetch_meta" ); + /* In lua, setting a variable to nil is equivalent to deleting it */ + lua_pushnil( L ); + lua_setglobal( L, "fetch_meta" ); /* Load and run the script(s) */ - if( luaL_dofile( p_state, psz_filename ) ) + if( luaL_dofile( L, psz_filename ) ) { msg_Warn( p_this, "Error loading script %s: %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); return VLC_EGENERIC; } - lua_getglobal( p_state, "fetch_meta" ); + lua_getglobal( L, "fetch_meta" ); - if( !lua_isfunction( p_state, lua_gettop( p_state ) ) ) + if( !lua_isfunction( L, lua_gettop( L ) ) ) { msg_Warn( p_this, "Error while runing script %s, " "function fetch_meta() not found", psz_filename ); - lua_pop( p_state, 1 ); + lua_pop( L, 1 ); return VLC_EGENERIC; } - if( lua_pcall( p_state, 0, 1, 0 ) ) + if( lua_pcall( L, 0, 1, 0 ) ) { msg_Warn( p_this, "Error while runing script %s, " "function fetch_meta(): %s", psz_filename, - lua_tostring( p_state, lua_gettop( p_state ) ) ); - lua_pop( p_state, 1 ); + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); return VLC_EGENERIC; } - if((t = lua_gettop( p_state ))) + if( lua_gettop( L ) ) { - if( lua_istable( p_state, t ) ) + if( lua_istable( L, -1 ) ) { - vlclua_read_meta_data( p_this, p_state, t, t+1, p_input ); - vlclua_read_custom_meta_data( p_this, p_state, t, t+1, p_input ); + /* ... item */ + vlclua_read_meta_data( p_this, L, p_input ); + vlclua_read_custom_meta_data( p_this, L, p_input ); } else { msg_Err( p_this, "Lua playlist script %s: " - "didn't return a table", psz_filename ); + "didn't return a table", psz_filename ); } } else @@ -267,10 +281,10 @@ int E_(FindMeta)( vlc_object_t *p_this ) { meta_engine_t *p_me = (meta_engine_t *)p_this; input_item_t *p_item = p_me->p_item; - lua_State *p_state = vlclua_meta_init( p_this, p_item ); + lua_State *L = vlclua_meta_init( p_this, p_item ); - int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, p_state, p_item ); - lua_close( p_state ); + int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, L, p_item ); + lua_close( L ); return i_ret; } @@ -281,10 +295,10 @@ int E_(FindArt)( vlc_object_t *p_this ) { playlist_t *p_playlist = (playlist_t *)p_this; input_item_t *p_item = (input_item_t *)(p_playlist->p_private); - lua_State *p_state = vlclua_meta_init( p_this, p_item ); + lua_State *L = vlclua_meta_init( p_this, p_item ); - int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, p_state, p_item ); - lua_close( p_state ); + int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, L, p_item ); + lua_close( L ); return i_ret; } diff --git a/modules/misc/lua/net.c b/modules/misc/lua/net.c new file mode 100644 index 0000000000..345d0f1f26 --- /dev/null +++ b/modules/misc/lua/net.c @@ -0,0 +1,207 @@ +/***************************************************************************** + * net.c: Network related functions + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +/***************************************************************************** + * + *****************************************************************************/ +int vlclua_url_parse( lua_State *L ) +{ + const char *psz_url = luaL_checkstring( L, 1 ); + const char *psz_option = luaL_optstring( L, 2, NULL ); + vlc_url_t url; + + vlc_UrlParse( &url, psz_url, psz_option?*psz_option:0 ); + + lua_newtable( L ); + lua_pushstring( L, url.psz_protocol ); + lua_setfield( L, -2, "protocol" ); + lua_pushstring( L, url.psz_username ); + lua_setfield( L, -2, "username" ); + lua_pushstring( L, url.psz_password ); + lua_setfield( L, -2, "password" ); + lua_pushstring( L, url.psz_host ); + lua_setfield( L, -2, "host" ); + lua_pushinteger( L, url.i_port ); + lua_setfield( L, -2, "port" ); + lua_pushstring( L, url.psz_path ); + lua_setfield( L, -2, "path" ); + lua_pushstring( L, url.psz_option ); + lua_setfield( L, -2, "option" ); + + vlc_UrlClean( &url ); + + return 1; +} + +int vlclua_net_listen_tcp( lua_State *L ) +{ + vlc_object_t *p_this = vlclua_get_this( L ); + const char *psz_host = luaL_checkstring( L, 1 ); + int i_port = luaL_checkint( L, 2 ); + int *pi_fd = net_ListenTCP( p_this, psz_host, i_port ); + if( pi_fd == NULL ) + return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port ); + lua_pushlightuserdata( L, pi_fd ); + return 1; +} + +int vlclua_net_listen_close( lua_State *L ) +{ + int *pi_fd = (int*)luaL_checklightuserdata( L, 1 ); + net_ListenClose( pi_fd ); + return 0; +} + +int vlclua_net_accept( lua_State *L ) +{ + vlc_object_t *p_this = vlclua_get_this( L ); + int *pi_fd = (int*)luaL_checklightuserdata( L, 1 ); + mtime_t i_wait = luaL_optint( L, 2, -1 ); /* default to block */ + int i_fd = net_Accept( p_this, pi_fd, i_wait ); + lua_pushinteger( L, i_fd ); + return 1; +} + +int vlclua_net_close( lua_State *L ) +{ + int i_fd = luaL_checkint( L, 1 ); + net_Close( i_fd ); + return 0; +} + +int vlclua_net_send( lua_State *L ) +{ + int i_fd = luaL_checkint( L, 1 ); + size_t i_len; + const char *psz_buffer = luaL_checklstring( L, 2, &i_len ); + i_len = luaL_optint( L, 3, i_len ); + i_len = send( i_fd, psz_buffer, i_len, 0 ); + lua_pushinteger( L, i_len ); + return 1; +} + +int vlclua_net_recv( lua_State *L ) +{ + int i_fd = luaL_checkint( L, 1 ); + size_t i_len = luaL_optint( L, 2, 1 ); + char psz_buffer[i_len]; + i_len = recv( i_fd, psz_buffer, i_len, 0 ); + lua_pushlstring( L, psz_buffer, i_len ); + return 1; +} + +int vlclua_net_select( lua_State *L ) +{ + int i_ret; + int i_nfds = luaL_checkint( L, 1 ); + fd_set *fds_read = (fd_set*)luaL_checkuserdata( L, 2, sizeof( fd_set ) ); + fd_set *fds_write = (fd_set*)luaL_checkuserdata( L, 3, sizeof( fd_set ) ); + double f_timeout = luaL_checknumber( L, 4 ); + struct timeval timeout; + timeout.tv_sec = (int)f_timeout; + timeout.tv_usec = (int)(1e6*(f_timeout-(double)((int)f_timeout))); + i_ret = select( i_nfds, fds_read, fds_write, 0, &timeout ); + lua_pushinteger( L, i_ret ); + lua_pushinteger( L, (double)timeout.tv_sec+((double)timeout.tv_usec)/1e-6 ); + return 2; +} + +int vlclua_fd_set_new( lua_State *L ) +{ + fd_set *fds = (fd_set*)lua_newuserdata( L, sizeof( fd_set ) ); + FD_ZERO( fds ); + return 1; +} + +int vlclua_fd_clr( lua_State *L ) +{ + fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) ); + int i_fd = luaL_checkint( L, 2 ); + FD_CLR( i_fd, fds ); + return 0; +} +int vlclua_fd_isset( lua_State *L ) +{ + fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) ); + int i_fd = luaL_checkint( L, 2 ); + lua_pushboolean( L, FD_ISSET( i_fd, fds ) ); + return 1; +} +int vlclua_fd_set( lua_State *L ) +{ + fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) ); + int i_fd = luaL_checkint( L, 2 ); + FD_SET( i_fd, fds ); + return 0; +} +int vlclua_fd_zero( lua_State *L ) +{ + fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) ); + FD_ZERO( fds ); + return 0; +} + +/* +int vlclua_fd_open( lua_State *L ) +{ +} +*/ + +int vlclua_fd_write( lua_State *L ) +{ + int i_fd = luaL_checkint( L, 1 ); + size_t i_len; + ssize_t i_ret; + const char *psz_buffer = luaL_checklstring( L, 2, &i_len ); + i_len = luaL_optint( L, 3, i_len ); + i_ret = write( i_fd, psz_buffer, i_len ); + lua_pushinteger( L, i_ret ); + return 1; +} + +int vlclua_fd_read( lua_State *L ) +{ + int i_fd = luaL_checkint( L, 1 ); + size_t i_len = luaL_optint( L, 2, 1 ); + char psz_buffer[i_len]; + i_len = read( i_fd, psz_buffer, i_len ); + lua_pushlstring( L, psz_buffer, i_len ); + return 1; +} diff --git a/modules/misc/lua/objects.c b/modules/misc/lua/objects.c new file mode 100644 index 0000000000..2e4dc40be4 --- /dev/null +++ b/modules/misc/lua/objects.c @@ -0,0 +1,218 @@ +/***************************************************************************** + * objects.c: Generic lua<->vlc object wrapper + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +typedef struct +{ + vlc_object_t *p_obj; +} vlclua_object_t; + +/***************************************************************************** + * Generic vlc_object_t wrapper creation + *****************************************************************************/ +int __vlclua_push_vlc_object( lua_State *L, vlc_object_t *p_obj, + lua_CFunction pf_gc ) +{ + vlclua_object_t *p_ud = (vlclua_object_t *) + lua_newuserdata( L, sizeof( vlclua_object_t ) ); + p_ud->p_obj = p_obj; + lua_newtable( L ); + /* Hide the metatable */ + lua_pushstring( L, "__metatable" ); + lua_pushstring( L, "none of your business" ); + lua_settable( L, -3 ); + if( pf_gc ) + { + /* Set the garbage collector if needed */ + lua_pushstring( L, "__gc" ); + lua_pushcfunction( L, pf_gc ); + lua_settable( L, -3 ); + } + lua_setmetatable( L, -2 ); + return 1; +} + +int vlclua_gc_release( lua_State *L ) +{ + vlclua_object_t *p_ud = (vlclua_object_t *)lua_touserdata( L, -1 ); + lua_pop( L, 1 ); + vlc_object_release( p_ud->p_obj ); + return 0; +} + +vlc_object_t *vlclua_checkobject( lua_State *L, int narg, int i_type ) +{ + vlclua_object_t *p_obj = (vlclua_object_t *)luaL_checkuserdata( L, narg, sizeof( vlclua_object_t ) ); + /* TODO: add some metatable based method to check that this isn't + * any userdata, but really some vlc object */ + if( i_type ) + { + if( p_obj->p_obj->i_object_type == i_type ) + return p_obj->p_obj; + else + { + luaL_error( L, "VLC object type doesn't match requirements." ); + return NULL; /* luaL_error alread longjmp-ed out of here. + * This is to make gcc happy */ + } + } + else + { + return p_obj->p_obj; + } +} + +static int vlc_object_type_from_string( const char *psz_name ) +{ + static const struct + { + int i_type; + const char *psz_name; + } pp_objects[] = + { { VLC_OBJECT_GLOBAL, "global" }, + { VLC_OBJECT_LIBVLC, "libvlc" }, + { VLC_OBJECT_MODULE, "module" }, + { VLC_OBJECT_INTF, "intf" }, + { VLC_OBJECT_PLAYLIST, "playlist" }, + { VLC_OBJECT_ITEM, "item" }, + { VLC_OBJECT_INPUT, "input" }, + { VLC_OBJECT_DECODER, "decoder" }, + { VLC_OBJECT_VOUT, "vout" }, + { VLC_OBJECT_AOUT, "aout" }, + { VLC_OBJECT_SOUT, "sout" }, + { VLC_OBJECT_HTTPD, "httpd" }, + { VLC_OBJECT_PACKETIZER, "packetizer" }, + { VLC_OBJECT_ENCODER, "encoder" }, + { VLC_OBJECT_DIALOGS, "dialogs" }, + { VLC_OBJECT_VLM, "vlm" }, + { VLC_OBJECT_ANNOUNCE, "announce" }, + { VLC_OBJECT_DEMUX, "demux" }, + { VLC_OBJECT_ACCESS, "access" }, + { VLC_OBJECT_STREAM, "stream" }, + { VLC_OBJECT_OPENGL, "opengl" }, + { VLC_OBJECT_FILTER, "filter" }, + { VLC_OBJECT_VOD, "vod" }, + { VLC_OBJECT_SPU, "spu" }, + { VLC_OBJECT_SD, "sd" }, + { VLC_OBJECT_XML, "xml" }, + { VLC_OBJECT_OSDMENU, "osdmenu" }, + { VLC_OBJECT_HTTPD_HOST, "httpd_host" }, + { VLC_OBJECT_META_ENGINE, "meta_engine" }, + { VLC_OBJECT_GENERIC, "generic" }, + { 0, "" } }; + int i; + for( i = 0; pp_objects[i].i_type; i++ ) + { + if( !strcmp( psz_name, pp_objects[i].psz_name ) ) + return pp_objects[i].i_type; + } + return 0; +} + +static int vlc_object_search_mode_from_string( const char *psz_name ) +{ + static const struct + { + int i_mode; + const char *psz_name; + } pp_modes[] = + { { FIND_PARENT, "parent" }, + { FIND_CHILD, "child" }, + { FIND_ANYWHERE, "anywhere" }, + { 0, "" } }; + int i; + for( i = 0; pp_modes[i].i_mode; i++ ) + { + if( !strcmp( psz_name, pp_modes[i].psz_name ) ) + return pp_modes[i].i_mode; + } + return 0; +} + +int vlclua_object_find( lua_State *L ) +{ + const char *psz_type = luaL_checkstring( L, 2 ); + const char *psz_mode = luaL_checkstring( L, 3 ); + + vlc_object_t *p_this; + int i_type = vlc_object_type_from_string( psz_type ); + int i_mode = vlc_object_search_mode_from_string( psz_mode ); + vlc_object_t *p_result; + + if( !i_type ) + return luaL_error( L, "\"%s\" is not a valid object type.", psz_type ); + if( !i_mode ) + return luaL_error( L, "\"%s\" is not a valid search mode.", psz_mode ); + + if( lua_type( L, 1 ) == LUA_TNIL ) + p_this = vlclua_get_this( L ); + else + p_this = vlclua_checkobject( L, 1, 0 ); + + p_result = vlc_object_find( p_this, i_type, i_mode ); + if( !p_result ) + lua_pushnil( L ); + else + vlclua_push_vlc_object( L, p_result, vlclua_gc_release ); + return 1; +} + +int vlclua_object_find_name( lua_State *L ) +{ + const char *psz_name = luaL_checkstring( L, 2 ); + const char *psz_mode = luaL_checkstring( L, 3 ); + + vlc_object_t *p_this; + int i_mode = vlc_object_search_mode_from_string( psz_mode ); + vlc_object_t *p_result; + + if( !i_mode ) + return luaL_error( L, "\"%s\" is not a valid search mode.", + psz_mode ); + + if( lua_type( L, 1 ) == LUA_TNIL ) + p_this = vlclua_get_this( L ); + else + p_this = vlclua_checkobject( L, 1, 0 ); + + p_result = vlc_object_find_name( p_this, psz_name, i_mode ); + if( !p_result ) + lua_pushnil( L ); + else + vlclua_push_vlc_object( L, p_result, vlclua_gc_release ); + return 1; +} diff --git a/modules/misc/lua/playlist.c b/modules/misc/lua/playlist.c new file mode 100644 index 0000000000..2090f918f6 --- /dev/null +++ b/modules/misc/lua/playlist.c @@ -0,0 +1,294 @@ +/***************************************************************************** + * playlist.c : Lua playlist demux module + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include /* ENOMEM */ +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include "vlc.h" + + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int Demux( demux_t *p_demux ); +static int Control( demux_t *p_demux, int i_query, va_list args ); + +/***************************************************************************** + * + *****************************************************************************/ +struct demux_sys_t +{ + lua_State *L; + char *psz_filename; +}; + +/***************************************************************************** + * + *****************************************************************************/ + +static int vlclua_demux_peek( lua_State *L ) +{ + demux_t *p_demux = (demux_t *)vlclua_get_this( L ); + int i = lua_gettop( L ); + int n; + const uint8_t *p_peek; + int i_peek; + if( !i ) return 0; + n = lua_tonumber( L, 1 ); + lua_pop( L, i ); + i_peek = stream_Peek( p_demux->s, &p_peek, n ); + lua_pushlstring( L, (const char *)p_peek, i_peek ); + return 1; +} + +static int vlclua_demux_read( lua_State *L ) +{ + demux_t *p_demux = (demux_t *)vlclua_get_this( L ); + int i = lua_gettop( L ); + int n; + byte_t *p_read; + int i_read; + if( !i ) return 0; + n = lua_tonumber( L, 1 ); + lua_pop( L, i ); + i_read = stream_Read( p_demux->s, &p_read, n ); + lua_pushlstring( L, (const char *)p_read, i_read ); + return 1; +} + +static int vlclua_demux_readline( lua_State *L ) +{ + demux_t *p_demux = (demux_t *)vlclua_get_this( L ); + char *psz_line = stream_ReadLine( p_demux->s ); + if( psz_line ) + { + lua_pushstring( L, psz_line ); + free( psz_line ); + } + else + { + lua_pushnil( L ); + } + return 1; +} + + +/* Functions to register */ +static luaL_Reg p_reg[] = +{ + { "peek", vlclua_demux_peek }, + { "decode_uri", vlclua_decode_uri }, + { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars }, + { "msg_dbg", vlclua_msg_dbg }, + { "msg_warn", vlclua_msg_warn }, + { "msg_err", vlclua_msg_err }, + { "msg_info", vlclua_msg_info }, + { NULL, NULL } +}; + +/* Functions to register for parse() function call only */ +static luaL_Reg p_reg_parse[] = +{ + { "read", vlclua_demux_read }, + { "readline", vlclua_demux_readline }, + { NULL, NULL } +}; + +/***************************************************************************** + * Called through lua_scripts_batch_execute to call 'probe' on + * the script pointed by psz_filename. + *****************************************************************************/ +static int probe_luascript( vlc_object_t *p_this, const char * psz_filename, + lua_State * L, void * user_data ) +{ + demux_t * p_demux = (demux_t *)p_this; + + p_demux->p_sys->psz_filename = strdup(psz_filename); + + /* In lua, setting a variable's value to nil is equivalent to deleting it */ + lua_pushnil( L ); + lua_pushnil( L ); + lua_setglobal( L, "probe" ); + lua_setglobal( L, "parse" ); + + /* Load and run the script(s) */ + if( luaL_dofile( L, psz_filename ) ) + { + msg_Warn( p_demux, "Error loading script %s: %s", psz_filename, + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); + return VLC_EGENERIC; + } + + lua_getglobal( L, "probe" ); + + if( !lua_isfunction( L, lua_gettop( L ) ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function probe() not found", psz_filename ); + lua_pop( L, 1 ); + return VLC_EGENERIC; + } + + if( lua_pcall( L, 0, 1, 0 ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function probe(): %s", psz_filename, + lua_tostring( L, lua_gettop( L ) ) ); + lua_pop( L, 1 ); + return VLC_EGENERIC; + } + + if( lua_gettop( L ) ) + { + int i_ret = VLC_EGENERIC; + if( lua_toboolean( L, 1 ) ) + { + msg_Dbg( p_demux, "Lua playlist script %s's " + "probe() function was successful", psz_filename ); + i_ret = VLC_SUCCESS; + } + lua_pop( L, 1 ); + + return i_ret; + } + return VLC_EGENERIC; +} + +/***************************************************************************** + * Import_LuaPlaylist: main import function + *****************************************************************************/ +int E_(Import_LuaPlaylist)( vlc_object_t *p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + lua_State *L; + + p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) ); + if( !p_demux->p_sys ) + { + return VLC_ENOMEM; + } + + p_demux->p_sys->psz_filename = NULL; + + p_demux->pf_control = Control; + p_demux->pf_demux = Demux; + + /* Initialise Lua state structure */ + L = luaL_newstate(); + if( !L ) + { + msg_Err( p_demux, "Could not create new Lua State" ); + free( p_demux->p_sys ); + return VLC_EGENERIC; + } + p_demux->p_sys->L = L; + + /* Load Lua libraries */ + luaL_openlibs( L ); /* FIXME: Don't open all the libs? */ + + luaL_register( L, "vlc", p_reg ); + lua_pushlightuserdata( L, p_demux ); + lua_setfield( L, -2, "private" ); + lua_pushstring( L, p_demux->psz_path ); + lua_setfield( L, -2, "path" ); + lua_pushstring( L, p_demux->psz_access ); + lua_setfield( L, -2, "access" ); + + lua_pop( L, 1 ); + + return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript, + L, NULL ); +} + + + +/***************************************************************************** + * Deactivate: frees unused data + *****************************************************************************/ +void E_(Close_LuaPlaylist)( vlc_object_t *p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + lua_close( p_demux->p_sys->L ); + free( p_demux->p_sys->psz_filename ); + free( p_demux->p_sys ); +} + +static int Demux( demux_t *p_demux ) +{ + lua_State *L = p_demux->p_sys->L; + char *psz_filename = p_demux->p_sys->psz_filename; + + playlist_t *p_playlist = pl_Yield( p_demux ); + input_thread_t *p_input_thread = (input_thread_t *) + vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT ); + input_item_t *p_current_input = input_GetItem( p_input_thread ); + + luaL_register( L, "vlc", p_reg_parse ); + + lua_getglobal( L, "parse" ); + + if( !lua_isfunction( L, lua_gettop( L ) ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function parse() not found", psz_filename ); + E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); + return VLC_EGENERIC; + } + + if( lua_pcall( L, 0, 1, 0 ) ) + { + msg_Warn( p_demux, "Error while runing script %s, " + "function parse(): %s", psz_filename, + lua_tostring( L, lua_gettop( L ) ) ); + E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) ); + return VLC_EGENERIC; + } + + if( lua_gettop( L ) ) + vlclua_playlist_add_internal( p_demux, L, p_playlist, + p_current_input, 0 ); + else + msg_Err( p_demux, "Script went completely foobar" ); + + vlc_object_release( p_input_thread ); + vlc_object_release( p_playlist ); + + return -1; /* Needed for correct operation of go back */ +} + +static int Control( demux_t *p_demux, int i_query, va_list args ) +{ + return VLC_EGENERIC; +} diff --git a/modules/misc/lua/variables.c b/modules/misc/lua/variables.c new file mode 100644 index 0000000000..ed0f93ca1e --- /dev/null +++ b/modules/misc/lua/variables.c @@ -0,0 +1,212 @@ +/***************************************************************************** + * variables.c: Generic lua<->vlc variables inteface + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +/***************************************************************************** + * Variables handling + *****************************************************************************/ +int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val ) +{ + switch( i_type &= 0xf0 ) + { + case VLC_VAR_VOID: + vlclua_error( L ); + break; + case VLC_VAR_BOOL: + lua_pushboolean( L, val.b_bool ); + break; + case VLC_VAR_INTEGER: + lua_pushinteger( L, val.i_int ); + break; + case VLC_VAR_STRING: + lua_pushstring( L, val.psz_string ); + break; + case VLC_VAR_FLOAT: + lua_pushnumber( L, val.f_float ); + break; + case VLC_VAR_TIME: + /* FIXME? (we're losing some precision, but does it really matter?) */ + lua_pushnumber( L, ((double)val.i_time)/1000000. ); + break; + case VLC_VAR_ADDRESS: + vlclua_error( L ); + break; + case VLC_VAR_MUTEX: + vlclua_error( L ); + break; + case VLC_VAR_LIST: + { + int i_count = val.p_list->i_count; + int i; + lua_createtable( L, i_count, 0 ); + for( i = 0; i < i_count; i++ ) + { + lua_pushinteger( L, i+1 ); + if( !vlclua_pushvalue( L, val.p_list->pi_types[i], + val.p_list->p_values[i] ) ) + lua_pushnil( L ); + lua_settable( L, -3 ); + } + } + break; + default: + vlclua_error( L ); + } + return 1; +} + +static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val ) +{ + switch( i_type & 0xf0 ) + { + case VLC_VAR_VOID: + break; + case VLC_VAR_BOOL: + val->b_bool = luaL_checkboolean( L, -1 ); + break; + case VLC_VAR_INTEGER: + val->i_int = luaL_checkint( L, -1 ); + break; + case VLC_VAR_STRING: + val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */ + break; + case VLC_VAR_FLOAT: + val->f_float = luaL_checknumber( L, -1 ); + break; + case VLC_VAR_TIME: + { + double f = luaL_checknumber( L, -1 ); + val->i_time = (vlc_int64_t)(f*1000000.); + } + break; + case VLC_VAR_ADDRESS: + vlclua_error( L ); + break; + case VLC_VAR_MUTEX: + vlclua_error( L ); + break; + case VLC_VAR_LIST: + vlclua_error( L ); + break; + default: + vlclua_error( L ); + } + return 1; +} + +int vlclua_var_get( lua_State *L ) +{ + int i_type; + vlc_value_t val; + vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 ); + const char *psz_var = luaL_checkstring( L, 2 ); + i_type = var_Type( p_obj, psz_var ); + var_Get( p_obj, psz_var, &val ); + lua_pop( L, 2 ); + return vlclua_pushvalue( L, i_type, val ); +} + +int vlclua_var_set( lua_State *L ) +{ + int i_type; + vlc_value_t val; + vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 ); + const char *psz_var = luaL_checkstring( L, 2 ); + int i_ret; + i_type = var_Type( p_obj, psz_var ); + vlclua_tovalue( L, i_type, &val ); + i_ret = var_Set( p_obj, psz_var, val ); + lua_pop( L, 3 ); + return vlclua_push_ret( L, i_ret ); +} + +int vlclua_var_get_list( lua_State *L ) +{ + vlc_value_t val; + vlc_value_t text; + vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 ); + const char *psz_var = luaL_checkstring( L, 2 ); + int i_ret = var_Change( p_obj, psz_var, VLC_VAR_GETLIST, &val, &text ); + if( i_ret < 0 ) return vlclua_push_ret( L, i_ret ); + vlclua_pushvalue( L, VLC_VAR_LIST, val ); + vlclua_pushvalue( L, VLC_VAR_LIST, text ); + var_Change( p_obj, psz_var, VLC_VAR_FREELIST, &val, &text ); + return 2; +} + +int vlclua_module_command( lua_State *L ) +{ + vlc_object_t * p_this = vlclua_get_this( L ); + const char *psz_name; + const char *psz_cmd; + const char *psz_arg; + char *psz_msg; + psz_name = luaL_checkstring( L, 1 ); + psz_cmd = luaL_checkstring( L, 2 ); + psz_arg = luaL_checkstring( L, 3 ); + lua_pop( L, 3 ); + var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg ); + if( psz_msg ) + { + lua_pushstring( L, psz_msg ); + free( psz_msg ); + } + else + { + lua_pushstring( L, "" ); + } + return 1; +} + +int vlclua_libvlc_command( lua_State *L ) +{ + vlc_object_t * p_this = vlclua_get_this( L ); + const char *psz_cmd; + vlc_value_t val_arg; + psz_cmd = luaL_checkstring( L, 1 ); + val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) ); + lua_pop( L, 2 ); + if( !var_Type( p_this->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND ) + { + free( val_arg.psz_string ); + return luaL_error( L, "libvlc's \"%s\" is not a command", + psz_cmd ); + } + + return vlclua_push_ret( L, + var_Set( p_this->p_libvlc, psz_cmd, val_arg ) ); +} diff --git a/modules/misc/lua/vlc.c b/modules/misc/lua/vlc.c new file mode 100644 index 0000000000..eb07f4fb9e --- /dev/null +++ b/modules/misc/lua/vlc.c @@ -0,0 +1,712 @@ +/***************************************************************************** + * vlc.c: Generic lua inteface functions + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +#include /* Low level lua C API */ +#include /* Higher level C API */ +#include /* Lua libs */ + +#include "vlc.h" + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ + +#define INTF_TEXT N_("Lua interface") +#define INTF_LONGTEXT N_("Lua interface module to load") + +#define CONFIG_TEXT N_("Lua inteface configuration") +#define CONFIG_LONGTEXT N_("Lua interface configuration string. Format is: '[\"\"] = {