X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=modules%2Fmisc%2Flua%2Fextension.c;h=51769ca86c51dbb9362c07df4cc452359e4de420;hb=83655cec3fe316a3f073c510688b881f10c787c1;hp=6c53651d6d171f3e166a2e6265b65d4302bbd615;hpb=47af2aa48ca5073f556da255e8497b1848a4c2a4;p=vlc diff --git a/modules/misc/lua/extension.c b/modules/misc/lua/extension.c index 6c53651d6d..51769ca86c 100644 --- a/modules/misc/lua/extension.c +++ b/modules/misc/lua/extension.c @@ -26,6 +26,9 @@ #include "extension.h" #include "assert.h" +#include +#include + /* Functions to register */ static const luaL_Reg p_reg[] = { @@ -36,18 +39,24 @@ static const luaL_Reg p_reg[] = * Extensions capabilities * Note: #define and ppsz_capabilities must be in sync */ -#define EXT_HAS_MENU (1 << 0) -#define EXT_TRIGGER_ONLY (1 << 1) +#define EXT_HAS_MENU (1 << 0) ///< Hook: menu +#define EXT_TRIGGER_ONLY (1 << 1) ///< Hook: trigger. Not activable +#define EXT_INPUT_LISTENER (1 << 2) ///< Hook: input_changed +#define EXT_META_LISTENER (1 << 3) ///< Hook: meta_changed +#define EXT_PLAYING_LISTENER (1 << 4) ///< Hook: status_changed const char* const ppsz_capabilities[] = { "menu", "trigger", + "input-listener", + "meta-listener", + "playing-listener", NULL }; static int ScanExtensions( extensions_manager_t *p_this ); static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, - lua_State *L, void *pb_continue ); + void *pb_continue ); static int Control( extensions_manager_t *, int, va_list ); static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext, char ***pppsz_titles, uint16_t **ppi_ids ); @@ -66,6 +75,10 @@ static int vlclua_extension_dialog_callback( vlc_object_t *p_this, vlc_value_t newval, void *p_data ); +/* Input item callback: vlc_InputItemMetaChanged */ +static void inputItemMetaChanged( const vlc_event_t *p_event, + void *data ); + /** * Module entry-point @@ -88,15 +101,6 @@ int Open_Extension( vlc_object_t *p_this ) vlc_mutex_init( &p_mgr->lock ); vlc_mutex_init( &p_mgr->p_sys->lock ); - /* Initialise Lua state structure */ - lua_State *L = GetLuaState( p_mgr, NULL ); - if( !L ) - { - free( p_sys ); - return VLC_EGENERIC; - } - p_sys->L = L; - /* Scan available Lua Extensions */ if( ScanExtensions( p_mgr ) != VLC_SUCCESS ) { @@ -104,9 +108,6 @@ int Open_Extension( vlc_object_t *p_this ) return VLC_EGENERIC; } - lua_close( L ); - p_sys->L = NULL; - // Create the dialog-event variable var_Create( p_this, "dialog-event", VLC_VAR_ADDRESS ); var_AddCallback( p_this, "dialog-event", @@ -133,6 +134,7 @@ void Close_Extension( vlc_object_t *p_this ) FOREACH_ARRAY( p_ext, p_mgr->p_sys->activated_extensions ) { if( !p_ext ) break; + msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title ); Deactivate( p_mgr, p_ext ); WaitForDeactivation( p_ext ); } @@ -141,9 +143,6 @@ void Close_Extension( vlc_object_t *p_this ) msg_Dbg( p_mgr, "All extensions are now deactivated" ); ARRAY_RESET( p_mgr->p_sys->activated_extensions ); - if( p_mgr->p_sys && p_mgr->p_sys->L ) - lua_close( p_mgr->p_sys->L ); - vlc_mutex_destroy( &p_mgr->lock ); vlc_mutex_destroy( &p_mgr->p_sys->lock ); free( p_mgr->p_sys ); @@ -158,6 +157,11 @@ void Close_Extension( vlc_object_t *p_this ) lua_close( p_ext->p_sys->L ); free( p_ext->psz_name ); free( p_ext->psz_title ); + free( p_ext->psz_author ); + free( p_ext->psz_description ); + free( p_ext->psz_shortdescription ); + free( p_ext->psz_url ); + free( p_ext->psz_version ); vlc_mutex_destroy( &p_ext->p_sys->running_lock ); vlc_mutex_destroy( &p_ext->p_sys->command_lock ); @@ -182,7 +186,7 @@ static int ScanExtensions( extensions_manager_t *p_mgr ) vlclua_scripts_batch_execute( VLC_OBJECT( p_mgr ), "extensions", &ScanLuaCallback, - p_mgr->p_sys->L, &b_true ); + &b_true ); if( !i_ret ) return VLC_EGENERIC; @@ -198,7 +202,7 @@ static int ScanExtensions( extensions_manager_t *p_mgr ) * @param pb_continue bool* that indicates whether to continue batch or not **/ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, - lua_State *L, void *pb_continue ) + void *pb_continue ) { extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this; bool b_ok = false; @@ -233,6 +237,7 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, vlc_cond_init( &p_ext->p_sys->wait ); /* Load and run the script(s) */ + lua_State *L = luaL_newstate(); if( luaL_dofile( L, psz_script ) ) { msg_Warn( p_mgr, "Error loading script %s: %s", psz_script, @@ -343,6 +348,18 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, } lua_pop( L, 1 ); + /* Get short description */ + lua_getfield( L, -1, "shortdesc" ); + if( lua_isstring( L, -1 ) ) + { + p_ext->psz_shortdescription = strdup( luaL_checkstring( L, -1 ) ); + } + else + { + p_ext->psz_shortdescription = NULL; + } + lua_pop( L, 1 ); + /* Get URL */ lua_getfield( L, -1, "url" ); if( lua_isstring( L, -1 ) ) @@ -385,10 +402,16 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, b_ok = true; exit: + lua_close( L ); if( !b_ok ) { free( p_ext->psz_name ); free( p_ext->psz_title ); + free( p_ext->psz_url ); + free( p_ext->psz_author ); + free( p_ext->psz_description ); + free( p_ext->psz_shortdescription ); + free( p_ext->psz_version ); vlc_mutex_destroy( &p_ext->p_sys->command_lock ); vlc_mutex_destroy( &p_ext->p_sys->running_lock ); vlc_cond_destroy( &p_ext->p_sys->wait ); @@ -458,9 +481,71 @@ static int Control( extensions_manager_t *p_mgr, int i_control, va_list args ) i = ( int ) va_arg( args, int ); return TriggerMenu( p_ext, i ); + case EXTENSION_SET_INPUT: + { + p_ext = ( extension_t* ) va_arg( args, extension_t* ); + input_thread_t *p_input = va_arg( args, struct input_thread_t * ); + + if( !LockExtension( p_ext ) ) + return VLC_EGENERIC; + + // Change input + input_thread_t *old = p_ext->p_sys->p_input; + input_item_t *p_item; + if( old ) + { + // Untrack meta fetched events + if( p_ext->p_sys->i_capabilities & EXT_META_LISTENER ) + { + p_item = input_GetItem( old ); + vlc_event_detach( &p_item->event_manager, + vlc_InputItemMetaChanged, + inputItemMetaChanged, + p_ext ); + vlc_gc_decref( p_item ); + } + vlc_object_release( old ); + } + + p_ext->p_sys->p_input = p_input ? vlc_object_hold( p_input ) + : p_input; + + // Tell the script the input changed + if( p_ext->p_sys->i_capabilities & EXT_INPUT_LISTENER ) + { + PushCommandUnique( p_ext, CMD_SET_INPUT ); + } + + // Track meta fetched events + if( p_ext->p_sys->p_input && + p_ext->p_sys->i_capabilities & EXT_META_LISTENER ) + { + p_item = input_GetItem( p_ext->p_sys->p_input ); + vlc_gc_incref( p_item ); + vlc_event_attach( &p_item->event_manager, + vlc_InputItemMetaChanged, + inputItemMetaChanged, + p_ext ); + } + + UnlockExtension( p_ext ); + break; + } + case EXTENSION_PLAYING_CHANGED: + { + extension_t *p_ext; + p_ext = ( extension_t* ) va_arg( args, extension_t* ); + assert( p_ext->psz_name != NULL ); + i = ( int ) va_arg( args, int ); + if( p_ext->p_sys->i_capabilities & EXT_PLAYING_LISTENER ) + { + PushCommand( p_ext, CMD_PLAYING_CHANGED, i ); + } + break; + } default: - msg_Err( p_mgr, "Control '%d' not yet implemented in Extension", - i_control ); + msg_Warn( p_mgr, "Control '%d' not yet implemented in Extension", + i_control ); return VLC_EGENERIC; } @@ -470,7 +555,7 @@ static int Control( extensions_manager_t *p_mgr, int i_control, va_list args ) int lua_ExtensionActivate( extensions_manager_t *p_mgr, extension_t *p_ext ) { assert( p_mgr != NULL && p_ext != NULL ); - return lua_ExecuteFunction( p_mgr, p_ext, "activate" ); + return lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ); } int lua_ExtensionDeactivate( extensions_manager_t *p_mgr, extension_t *p_ext ) @@ -480,7 +565,19 @@ int lua_ExtensionDeactivate( extensions_manager_t *p_mgr, extension_t *p_ext ) if( !p_ext->p_sys->L ) return VLC_SUCCESS; - int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "deactivate" ); + // Unset and release input objects + if( p_ext->p_sys->p_input ) + { + if( p_ext->p_sys->i_capabilities & EXT_META_LISTENER ) + { + // Release item + input_item_t *p_item = input_GetItem( p_ext->p_sys->p_input ); + vlc_gc_decref( p_item ); + } + vlc_object_release( p_ext->p_sys->p_input ); + } + + int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "deactivate", LUA_END ); /* Clear Lua State */ lua_close( p_ext->p_sys->L ); @@ -496,7 +593,10 @@ int lua_ExtensionWidgetClick( extensions_manager_t *p_mgr, if( !p_ext->p_sys->L ) return VLC_SUCCESS; - return lua_ExecuteFunction( p_mgr, p_ext, (const char*) p_widget->p_sys ); + lua_State *L = GetLuaState( p_mgr, p_ext ); + lua_pushlightuserdata( L, p_widget ); + lua_gettable( L, LUA_REGISTRYINDEX ); + return lua_ExecuteFunction( p_mgr, p_ext, NULL, LUA_END ); } @@ -622,16 +722,13 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr, msg_Err( p_mgr, "Could not create new Lua State" ); return NULL; } + vlclua_set_this( L, p_mgr ); + vlclua_extension_set( L, p_ext ); + luaL_openlibs( L ); luaL_register( L, "vlc", p_reg ); luaopen_msg( L ); - lua_pushlightuserdata( L, p_mgr ); - lua_setfield( L, -2, "private" ); - - lua_pushlightuserdata( L, p_ext ); - lua_setfield( L, -2, "extension" ); - if( p_ext ) { /* Load more libraries */ @@ -639,6 +736,7 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr, luaopen_config( L ); luaopen_dialog( L, p_ext ); luaopen_input( L ); + luaopen_md5( L ); luaopen_msg( L ); luaopen_misc( L ); luaopen_net( L ); @@ -652,12 +750,20 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr, luaopen_video( L ); luaopen_vlm( L ); luaopen_volume( L ); + luaopen_xml( L ); /* Register extension specific functions */ lua_getglobal( L, "vlc" ); lua_pushcfunction( L, vlclua_extension_deactivate ); lua_setfield( L, -2, "deactivate" ); + /* Setup the module search path */ + if( vlclua_add_modules_path( p_mgr, L, p_ext->psz_name ) ) + { + msg_Warn( p_mgr, "Error while setting the module search path for %s", p_ext->psz_name ); + return NULL; + } + /* Load and run the script(s) */ if( luaL_dofile( L, p_ext->psz_name ) != 0 ) { @@ -681,22 +787,36 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr, return L; } +int lua_ExecuteFunction( extensions_manager_t *p_mgr, extension_t *p_ext, + const char *psz_function, ... ) +{ + va_list args; + va_start( args, psz_function ); + int i_ret = lua_ExecuteFunctionVa( p_mgr, p_ext, psz_function, args ); + va_end( args ); + return i_ret; +} + /** * Execute a function in a Lua script + * @param psz_function Name of global function to execute. If NULL, assume + * that the function object is already on top of the + * stack. * @return < 0 in case of failure, >= 0 in case of success * @note It's better to call this function from a dedicated thread * (see extension_thread.c) **/ -int lua_ExecuteFunction( extensions_manager_t *p_mgr, - extension_t *p_ext, - const char *psz_function ) +int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext, + const char *psz_function, va_list args ) { int i_ret = VLC_EGENERIC; + int i_args = 0; assert( p_mgr != NULL ); assert( p_ext != NULL ); lua_State *L = GetLuaState( p_mgr, p_ext ); - lua_getglobal( L, psz_function ); + if( psz_function ) + lua_getglobal( L, psz_function ); if( !lua_isfunction( L, -1 ) ) { @@ -705,7 +825,26 @@ int lua_ExecuteFunction( extensions_manager_t *p_mgr, goto exit; } - if( lua_pcall( L, 0, 1, 0 ) ) + lua_datatype_e type = LUA_END; + while( ( type = va_arg( args, int ) ) != LUA_END ) + { + if( type == LUA_NUM ) + { + lua_pushnumber( L , ( int ) va_arg( args, int ) ); + } + else if( type == LUA_TEXT ) + { + lua_pushstring( L , ( char * ) va_arg( args, char* ) ); + } + else + { + msg_Warn( p_mgr, "Undefined argument type %d to lua function %s" + "from script %s", type, psz_function, p_ext->psz_name ); + goto exit; + } + i_args ++; + } + if( lua_pcall( L, i_args, 1, 0 ) ) { msg_Warn( p_mgr, "Error while runing script %s, " "function %s(): %s", p_ext->psz_name, psz_function, @@ -713,9 +852,10 @@ int lua_ExecuteFunction( extensions_manager_t *p_mgr, goto exit; } - i_ret = VLC_SUCCESS; + i_ret = lua_DialogFlush( L ); exit: return i_ret; + } static inline int TriggerMenu( extension_t *p_ext, int i_id ) @@ -753,6 +893,7 @@ int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr, return VLC_EGENERIC; } + i_ret = lua_DialogFlush( L ); if( i_ret < VLC_SUCCESS ) { msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)", @@ -770,7 +911,7 @@ int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr, static int TriggerExtension( extensions_manager_t *p_mgr, extension_t *p_ext ) { - int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "trigger" ); + int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "trigger", LUA_END ); /* Close lua state for trigger-only extensions */ if( p_ext->p_sys->L ) @@ -780,17 +921,27 @@ static int TriggerExtension( extensions_manager_t *p_mgr, return i_ret; } +/** Set extension associated to the current script + * @param L current lua_State + * @param p_ext the extension + */ +void vlclua_extension_set( lua_State *L, extension_t *p_ext ) +{ + lua_pushlightuserdata( L, vlclua_extension_set ); + lua_pushlightuserdata( L, p_ext ); + lua_rawset( L, LUA_REGISTRYINDEX ); +} + /** Retrieve extension associated to the current script * @param L current lua_State - * @return Lua userdata "vlc.extension" + * @return Extension pointer **/ extension_t *vlclua_extension_get( lua_State *L ) { - extension_t *p_ext = NULL; - lua_getglobal( L, "vlc" ); - lua_getfield( L, -1, "extension" ); - p_ext = (extension_t*) lua_topointer( L, lua_gettop( L ) ); - lua_pop( L, 2 ); + lua_pushlightuserdata( L, vlclua_extension_set ); + lua_rawget( L, LUA_REGISTRYINDEX ); + extension_t *p_ext = (extension_t*) lua_topointer( L, -1 ); + lua_pop( L, 1 ); return p_ext; } @@ -840,7 +991,7 @@ static int vlclua_extension_dialog_callback( vlc_object_t *p_this, PushCommand( p_ext, CMD_CLICK, p_widget ); break; case EXTENSION_EVENT_CLOSE: - PushCommand( p_ext, CMD_CLOSE ); + PushCommandUnique( p_ext, CMD_CLOSE ); break; default: msg_Dbg( p_this, "Received unknown UI event %d, discarded", @@ -851,6 +1002,19 @@ static int vlclua_extension_dialog_callback( vlc_object_t *p_this, return VLC_SUCCESS; } +/** Callback on vlc_InputItemMetaChanged event + **/ +static void inputItemMetaChanged( const vlc_event_t *p_event, + void *data ) +{ + assert( p_event && p_event->type == vlc_InputItemMetaChanged ); + + extension_t *p_ext = ( extension_t* ) data; + assert( p_ext != NULL ); + + PushCommandUnique( p_ext, CMD_UPDATE_META ); +} + /* Lock this extension. Can fail. */ bool LockExtension( extension_t *p_ext ) {