#include "extension.h"
#include "assert.h"
+#include <vlc_input.h>
+#include <vlc_events.h>
+
/* Functions to register */
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
const char* const ppsz_capabilities[] = {
"menu",
"trigger",
+ "input-listener",
+ "meta-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 );
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
p_mgr->p_sys = p_sys;
ARRAY_INIT( p_sys->activated_extensions );
ARRAY_INIT( p_mgr->extensions );
+ 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 )
{
return VLC_EGENERIC;
}
- lua_close( L );
- p_sys->L = NULL;
-
- vlc_mutex_init( &p_sys->lock );
-
// Create the dialog-event variable
var_Create( p_this, "dialog-event", VLC_VAR_ADDRESS );
var_AddCallback( p_this, "dialog-event",
extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
msg_Dbg( p_mgr, "Deactivating all loaded extensions" );
- vlc_mutex_lock( &p_mgr->p_sys->lock );
+ vlc_mutex_lock( &p_mgr->lock );
p_mgr->p_sys->b_killed = true;
- vlc_mutex_unlock( &p_mgr->p_sys->lock );
+ vlc_mutex_unlock( &p_mgr->lock );
+
+ var_Destroy( p_mgr, "dialog-event" );
extension_t *p_ext = NULL;
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 );
}
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 );
p_mgr->p_sys = NULL;
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 );
FOREACH_END()
ARRAY_RESET( p_mgr->extensions );
-
- var_Destroy( p_mgr, "dialog-event" );
}
/**
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;
* @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;
msg_Dbg( p_mgr, "Scanning Lua script %s", psz_script );
- vlc_mutex_lock( &p_mgr->p_sys->lock );
+ vlc_mutex_lock( &p_mgr->lock );
/* Create new script descriptor */
extension_t *p_ext = ( extension_t* ) calloc( 1, sizeof( extension_t ) );
if( !p_ext )
{
- vlc_mutex_unlock( &p_mgr->p_sys->lock );
+ vlc_mutex_unlock( &p_mgr->lock );
return 0;
}
free( p_ext->psz_name );
free( p_ext->p_sys );
free( p_ext );
- vlc_mutex_unlock( &p_mgr->p_sys->lock );
+ vlc_mutex_unlock( &p_mgr->lock );
return 0;
}
p_ext->p_sys->p_mgr = p_mgr;
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,
{
if( lua_istable( L, -1 ) )
{
+ /* Get caps */
lua_getfield( L, -1, "capabilities" );
if( lua_istable( L, -1 ) )
{
"did not return a table of capabilities.",
psz_script );
}
-
lua_pop( L, 1 );
+
+ /* Get title */
lua_getfield( L, -1, "title" );
if( lua_isstring( L, -1 ) )
{
psz_script );
p_ext->psz_title = strdup( psz_script );
}
+ lua_pop( L, 1 );
+
+ /* Get author */
+ lua_getfield( L, -1, "author" );
+ if( lua_isstring( L, -1 ) )
+ {
+ p_ext->psz_author = strdup( luaL_checkstring( L, -1 ) );
+ }
+ else
+ {
+ p_ext->psz_author = NULL;
+ }
+ lua_pop( L, 1 );
+
+ /* Get description */
+ lua_getfield( L, -1, "description" );
+ if( lua_isstring( L, -1 ) )
+ {
+ p_ext->psz_description = strdup( luaL_checkstring( L, -1 ) );
+ }
+ else
+ {
+ p_ext->psz_description = NULL;
+ }
+ 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 ) )
+ {
+ p_ext->psz_url = strdup( luaL_checkstring( L, -1 ) );
+ }
+ else
+ {
+ p_ext->psz_url = NULL;
+ }
+ lua_pop( L, 1 );
+
+ /* Get version */
+ lua_getfield( L, -1, "version" );
+ if( lua_isstring( L, -1 ) )
+ {
+ p_ext->psz_version = strdup( luaL_checkstring( L, -1 ) );
+ }
+ else
+ {
+ p_ext->psz_version = NULL;
+ }
+ lua_pop( L, 1 );
}
else
{
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 );
ARRAY_APPEND( p_mgr->extensions, p_ext );
}
- vlc_mutex_unlock( &p_mgr->p_sys->lock );
+ vlc_mutex_unlock( &p_mgr->lock );
/* Continue batch execution */
return pb_continue ? ( (* (bool*)pb_continue) ? -1 : 0 ) : -1;
}
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;
+ }
+
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;
}
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 )
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 );
if( !p_ext->p_sys->L )
return VLC_SUCCESS;
- return lua_ExecuteFunction( p_mgr, p_ext, (const char*) p_widget->p_sys );
+ return lua_ExecuteFunction( p_mgr, p_ext, (const char*) p_widget->p_sys, LUA_END );
}
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 )
{
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
* @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 );
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,
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 )
return VLC_EGENERIC;
}
+ i_ret = lua_DialogFlush( L );
if( i_ret < VLC_SUCCESS )
{
msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
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 )
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",
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 )
{