]> git.sesse.net Git - vlc/blob - modules/misc/lua/extension.c
Lua: Allow arguments to be passed to lua functions in lua_ExecuteFunction
[vlc] / modules / misc / lua / extension.c
1 /*****************************************************************************
2  * extension.c: Lua Extensions (meta data, web information, ...)
3  *****************************************************************************
4  * Copyright (C) 2009-2010 VideoLAN and authors
5  * $Id$
6  *
7  * Authors: Jean-Philippe AndrĂ© < jpeg # videolan.org >
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include "vlc.h"
25 #include "libs.h"
26 #include "extension.h"
27 #include "assert.h"
28
29 #include <vlc_input.h>
30 #include <vlc_events.h>
31
32 /* Functions to register */
33 static const luaL_Reg p_reg[] =
34 {
35     { NULL, NULL }
36 };
37
38 /*
39  * Extensions capabilities
40  * Note: #define and ppsz_capabilities must be in sync
41  */
42 #define EXT_HAS_MENU          (1 << 0)   ///< Hook: menu
43 #define EXT_TRIGGER_ONLY      (1 << 1)   ///< Hook: trigger. Not activable
44 #define EXT_INPUT_LISTENER    (1 << 2)   ///< Hook: input_changed
45 #define EXT_META_LISTENER     (1 << 3)   ///< Hook: meta_changed
46
47 const char* const ppsz_capabilities[] = {
48     "menu",
49     "trigger",
50     "input-listener",
51     "meta-listener",
52     NULL
53 };
54
55 static int ScanExtensions( extensions_manager_t *p_this );
56 static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
57                             void *pb_continue );
58 static int Control( extensions_manager_t *, int, va_list );
59 static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
60                     char ***pppsz_titles, uint16_t **ppi_ids );
61 static lua_State* GetLuaState( extensions_manager_t *p_mgr,
62                                extension_t *p_ext );
63 static int TriggerMenu( extension_t *p_ext, int id );
64 static int TriggerExtension( extensions_manager_t *p_mgr,
65                              extension_t *p_ext );
66
67 int vlclua_extension_deactivate( lua_State *L );
68
69 /* Interactions */
70 static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
71                                              char const *psz_var,
72                                              vlc_value_t oldval,
73                                              vlc_value_t newval,
74                                              void *p_data );
75
76 /* Input item callback: vlc_InputItemMetaChanged */
77 static void inputItemMetaChanged( const vlc_event_t *p_event,
78                                   void *data );
79
80
81 /**
82  * Module entry-point
83  **/
84 int Open_Extension( vlc_object_t *p_this )
85 {
86     msg_Dbg( p_this, "Opening EXPERIMENTAL Lua Extension module" );
87
88     extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
89
90     p_mgr->pf_control = Control;
91
92     extensions_manager_sys_t *p_sys = ( extensions_manager_sys_t* )
93                     calloc( 1, sizeof( extensions_manager_sys_t ) );
94     if( !p_sys ) return VLC_ENOMEM;
95
96     p_mgr->p_sys = p_sys;
97     ARRAY_INIT( p_sys->activated_extensions );
98     ARRAY_INIT( p_mgr->extensions );
99     vlc_mutex_init( &p_mgr->lock );
100     vlc_mutex_init( &p_mgr->p_sys->lock );
101
102     /* Scan available Lua Extensions */
103     if( ScanExtensions( p_mgr ) != VLC_SUCCESS )
104     {
105         msg_Err( p_mgr, "Can't load extensions modules" );
106         return VLC_EGENERIC;
107     }
108
109     // Create the dialog-event variable
110     var_Create( p_this, "dialog-event", VLC_VAR_ADDRESS );
111     var_AddCallback( p_this, "dialog-event",
112                      vlclua_extension_dialog_callback, NULL );
113
114     return VLC_SUCCESS;
115 }
116
117 /**
118  * Module unload function
119  **/
120 void Close_Extension( vlc_object_t *p_this )
121 {
122     extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
123     msg_Dbg( p_mgr, "Deactivating all loaded extensions" );
124
125     vlc_mutex_lock( &p_mgr->lock );
126     p_mgr->p_sys->b_killed = true;
127     vlc_mutex_unlock( &p_mgr->lock );
128
129     var_Destroy( p_mgr, "dialog-event" );
130
131     extension_t *p_ext = NULL;
132     FOREACH_ARRAY( p_ext, p_mgr->p_sys->activated_extensions )
133     {
134         if( !p_ext ) break;
135         msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
136         Deactivate( p_mgr, p_ext );
137         WaitForDeactivation( p_ext );
138     }
139     FOREACH_END()
140
141     msg_Dbg( p_mgr, "All extensions are now deactivated" );
142     ARRAY_RESET( p_mgr->p_sys->activated_extensions );
143
144     vlc_mutex_destroy( &p_mgr->lock );
145     vlc_mutex_destroy( &p_mgr->p_sys->lock );
146     free( p_mgr->p_sys );
147     p_mgr->p_sys = NULL;
148
149     /* Free extensions' memory */
150     FOREACH_ARRAY( p_ext, p_mgr->extensions )
151     {
152         if( !p_ext )
153             break;
154         if( p_ext->p_sys->L )
155             lua_close( p_ext->p_sys->L );
156         free( p_ext->psz_name );
157         free( p_ext->psz_title );
158         free( p_ext->psz_author );
159         free( p_ext->psz_description );
160         free( p_ext->psz_shortdescription );
161         free( p_ext->psz_url );
162         free( p_ext->psz_version );
163
164         vlc_mutex_destroy( &p_ext->p_sys->running_lock );
165         vlc_mutex_destroy( &p_ext->p_sys->command_lock );
166         vlc_cond_destroy( &p_ext->p_sys->wait );
167
168         free( p_ext->p_sys );
169         free( p_ext );
170     }
171     FOREACH_END()
172
173     ARRAY_RESET( p_mgr->extensions );
174 }
175
176 /**
177  * Batch scan all Lua files in folder "extensions"
178  * @param p_mgr This extensions_manager_t object
179  **/
180 static int ScanExtensions( extensions_manager_t *p_mgr )
181 {
182     bool b_true = true;
183     int i_ret =
184         vlclua_scripts_batch_execute( VLC_OBJECT( p_mgr ),
185                                       "extensions",
186                                       &ScanLuaCallback,
187                                       &b_true );
188
189     if( !i_ret )
190         return VLC_EGENERIC;
191
192     return VLC_SUCCESS;
193 }
194
195 /**
196  * Batch scan all Lua files in folder "extensions": callback
197  * @param p_this This extensions_manager_t object
198  * @param psz_script Name of the script to run
199  * @param L Lua State, common to all scripts here
200  * @param pb_continue bool* that indicates whether to continue batch or not
201  **/
202 int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
203                      void *pb_continue )
204 {
205     extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
206     bool b_ok = false;
207
208     msg_Dbg( p_mgr, "Scanning Lua script %s", psz_script );
209
210     vlc_mutex_lock( &p_mgr->lock );
211
212     /* Create new script descriptor */
213     extension_t *p_ext = ( extension_t* ) calloc( 1, sizeof( extension_t ) );
214     if( !p_ext )
215     {
216         vlc_mutex_unlock( &p_mgr->lock );
217         return 0;
218     }
219
220     p_ext->psz_name = strdup( psz_script );
221     p_ext->p_sys = (extension_sys_t*) calloc( 1, sizeof( extension_sys_t ) );
222     if( !p_ext->p_sys || !p_ext->psz_name )
223     {
224         free( p_ext->psz_name );
225         free( p_ext->p_sys );
226         free( p_ext );
227         vlc_mutex_unlock( &p_mgr->lock );
228         return 0;
229     }
230     p_ext->p_sys->p_mgr = p_mgr;
231
232     /* Mutexes and conditions */
233     vlc_mutex_init( &p_ext->p_sys->command_lock );
234     vlc_mutex_init( &p_ext->p_sys->running_lock );
235     vlc_cond_init( &p_ext->p_sys->wait );
236
237     /* Load and run the script(s) */
238     lua_State *L = luaL_newstate();
239     if( luaL_dofile( L, psz_script ) )
240     {
241         msg_Warn( p_mgr, "Error loading script %s: %s", psz_script,
242                   lua_tostring( L, lua_gettop( L ) ) );
243         lua_pop( L, 1 );
244         goto exit;
245     }
246
247     /* Scan script for capabilities */
248     lua_getglobal( L, "descriptor" );
249
250     if( !lua_isfunction( L, -1 ) )
251     {
252         msg_Warn( p_mgr, "Error while runing script %s, "
253                   "function descriptor() not found", psz_script );
254         goto exit;
255     }
256
257     if( lua_pcall( L, 0, 1, 0 ) )
258     {
259         msg_Warn( p_mgr, "Error while runing script %s, "
260                   "function descriptor(): %s", psz_script,
261                   lua_tostring( L, lua_gettop( L ) ) );
262         goto exit;
263     }
264
265     if( lua_gettop( L ) )
266     {
267         if( lua_istable( L, -1 ) )
268         {
269             /* Get caps */
270             lua_getfield( L, -1, "capabilities" );
271             if( lua_istable( L, -1 ) )
272             {
273                 lua_pushnil( L );
274                 while( lua_next( L, -2 ) != 0 )
275                 {
276                     /* Key is at index -2 and value at index -1. Discard key */
277                     const char *psz_cap = luaL_checkstring( L, -1 );
278                     int i_cap = 0;
279                     bool b_ok = false;
280                     /* Find this capability's flag */
281                     for( const char *iter = *ppsz_capabilities;
282                          iter != NULL;
283                          iter = ppsz_capabilities[ ++i_cap ])
284                     {
285                         if( !strcmp( iter, psz_cap ) )
286                         {
287                             /* Flag it! */
288                             p_ext->p_sys->i_capabilities |= 1 << i_cap;
289                             b_ok = true;
290                             break;
291                         }
292                     }
293                     if( !b_ok )
294                     {
295                         msg_Warn( p_mgr, "Extension capability '%s' unknown in"
296                                   " script %s", psz_cap, psz_script );
297                     }
298                     /* Removes 'value'; keeps 'key' for next iteration */
299                     lua_pop( L, 1 );
300                 }
301             }
302             else
303             {
304                 msg_Warn( p_mgr, "In script %s, function descriptor() "
305                               "did not return a table of capabilities.",
306                               psz_script );
307             }
308             lua_pop( L, 1 );
309
310             /* Get title */
311             lua_getfield( L, -1, "title" );
312             if( lua_isstring( L, -1 ) )
313             {
314                 p_ext->psz_title = strdup( luaL_checkstring( L, -1 ) );
315             }
316             else
317             {
318                 msg_Dbg( p_mgr, "In script %s, function descriptor() "
319                                 "did not return a string as title.",
320                                 psz_script );
321                 p_ext->psz_title = strdup( psz_script );
322             }
323             lua_pop( L, 1 );
324
325             /* Get author */
326             lua_getfield( L, -1, "author" );
327             if( lua_isstring( L, -1 ) )
328             {
329                 p_ext->psz_author = strdup( luaL_checkstring( L, -1 ) );
330             }
331             else
332             {
333                 p_ext->psz_author = NULL;
334             }
335             lua_pop( L, 1 );
336
337             /* Get description */
338             lua_getfield( L, -1, "description" );
339             if( lua_isstring( L, -1 ) )
340             {
341                 p_ext->psz_description = strdup( luaL_checkstring( L, -1 ) );
342             }
343             else
344             {
345                 p_ext->psz_description = NULL;
346             }
347             lua_pop( L, 1 );
348
349             /* Get short description */
350             lua_getfield( L, -1, "shortdesc" );
351             if( lua_isstring( L, -1 ) )
352             {
353                 p_ext->psz_shortdescription = strdup( luaL_checkstring( L, -1 ) );
354             }
355             else
356             {
357                 p_ext->psz_shortdescription = NULL;
358             }
359             lua_pop( L, 1 );
360
361             /* Get URL */
362             lua_getfield( L, -1, "url" );
363             if( lua_isstring( L, -1 ) )
364             {
365                 p_ext->psz_url = strdup( luaL_checkstring( L, -1 ) );
366             }
367             else
368             {
369                 p_ext->psz_url = NULL;
370             }
371             lua_pop( L, 1 );
372
373             /* Get version */
374             lua_getfield( L, -1, "version" );
375             if( lua_isstring( L, -1 ) )
376             {
377                 p_ext->psz_version = strdup( luaL_checkstring( L, -1 ) );
378             }
379             else
380             {
381                 p_ext->psz_version = NULL;
382             }
383             lua_pop( L, 1 );
384         }
385         else
386         {
387             msg_Warn( p_mgr, "In script %s, function descriptor() "
388                       "did not return a table!", psz_script );
389             goto exit;
390         }
391     }
392     else
393     {
394         msg_Err( p_mgr, "Script %s went completely foobar", psz_script );
395         goto exit;
396     }
397
398     msg_Dbg( p_mgr, "Script %s has the following capability flags: 0x%x",
399              psz_script, p_ext->p_sys->i_capabilities );
400
401     b_ok = true;
402 exit:
403     lua_close( L );
404     if( !b_ok )
405     {
406         free( p_ext->psz_name );
407         free( p_ext->psz_title );
408         free( p_ext->psz_url );
409         free( p_ext->psz_author );
410         free( p_ext->psz_description );
411         free( p_ext->psz_shortdescription );
412         free( p_ext->psz_version );
413         vlc_mutex_destroy( &p_ext->p_sys->command_lock );
414         vlc_mutex_destroy( &p_ext->p_sys->running_lock );
415         vlc_cond_destroy( &p_ext->p_sys->wait );
416         free( p_ext->p_sys );
417         free( p_ext );
418     }
419     else
420     {
421         /* Add the extension to the list of known extensions */
422         ARRAY_APPEND( p_mgr->extensions, p_ext );
423     }
424
425     vlc_mutex_unlock( &p_mgr->lock );
426     /* Continue batch execution */
427     return pb_continue ? ( (* (bool*)pb_continue) ? -1 : 0 ) : -1;
428 }
429
430 static int Control( extensions_manager_t *p_mgr, int i_control, va_list args )
431 {
432     extension_t *p_ext = NULL;
433     bool *pb = NULL;
434     uint16_t **ppus = NULL;
435     char ***pppsz = NULL;
436     int i = 0;
437
438     switch( i_control )
439     {
440         case EXTENSION_ACTIVATE:
441             p_ext = ( extension_t* ) va_arg( args, extension_t* );
442             return Activate( p_mgr, p_ext );
443
444         case EXTENSION_DEACTIVATE:
445             p_ext = ( extension_t* ) va_arg( args, extension_t* );
446             return Deactivate( p_mgr, p_ext );
447
448         case EXTENSION_IS_ACTIVATED:
449             p_ext = ( extension_t* ) va_arg( args, extension_t* );
450             pb = ( bool* ) va_arg( args, bool* );
451             *pb = IsActivated( p_mgr, p_ext );
452             break;
453
454         case EXTENSION_HAS_MENU:
455             p_ext = ( extension_t* ) va_arg( args, extension_t* );
456             pb = ( bool* ) va_arg( args, bool* );
457             *pb = ( p_ext->p_sys->i_capabilities & EXT_HAS_MENU ) ? 1 : 0;
458             break;
459
460         case EXTENSION_GET_MENU:
461             p_ext = ( extension_t* ) va_arg( args, extension_t* );
462             pppsz = ( char*** ) va_arg( args, char*** );
463             ppus = ( uint16_t** ) va_arg( args, uint16_t** );
464             return GetMenuEntries( p_mgr, p_ext, pppsz, ppus );
465
466         case EXTENSION_TRIGGER_ONLY:
467             p_ext = ( extension_t* ) va_arg( args, extension_t* );
468             pb = ( bool* ) va_arg( args, bool* );
469             *pb = ( p_ext->p_sys->i_capabilities & EXT_TRIGGER_ONLY ) ? 1 : 0;
470             break;
471
472         case EXTENSION_TRIGGER:
473             p_ext = ( extension_t* ) va_arg( args, extension_t* );
474             return TriggerExtension( p_mgr, p_ext );
475
476         case EXTENSION_TRIGGER_MENU:
477             p_ext = ( extension_t* ) va_arg( args, extension_t* );
478             // GCC: 'uint16_t' is promoted to 'int' when passed through '...'
479             i = ( int ) va_arg( args, int );
480             return TriggerMenu( p_ext, i );
481
482         case EXTENSION_SET_INPUT:
483         {
484             p_ext = ( extension_t* ) va_arg( args, extension_t* );
485             input_thread_t *p_input = va_arg( args, struct input_thread_t * );
486
487             if( !LockExtension( p_ext ) )
488                 return VLC_EGENERIC;
489
490             // Change input
491             input_thread_t *old = p_ext->p_sys->p_input;
492             input_item_t *p_item;
493             if( old )
494             {
495                 // Untrack meta fetched events
496                 if( p_ext->p_sys->i_capabilities & EXT_META_LISTENER )
497                 {
498                     p_item = input_GetItem( old );
499                     vlc_event_detach( &p_item->event_manager,
500                                       vlc_InputItemMetaChanged,
501                                       inputItemMetaChanged,
502                                       p_ext );
503                     vlc_gc_decref( p_item );
504                 }
505                 vlc_object_release( old );
506             }
507
508             p_ext->p_sys->p_input = p_input ? vlc_object_hold( p_input )
509                                             : p_input;
510
511             // Tell the script the input changed
512             if( p_ext->p_sys->i_capabilities & EXT_INPUT_LISTENER )
513             {
514                 PushCommandUnique( p_ext, CMD_SET_INPUT );
515             }
516
517             // Track meta fetched events
518             if( p_ext->p_sys->p_input &&
519                 p_ext->p_sys->i_capabilities & EXT_META_LISTENER )
520             {
521                 p_item = input_GetItem( p_ext->p_sys->p_input );
522                 vlc_gc_incref( p_item );
523                 vlc_event_attach( &p_item->event_manager,
524                                   vlc_InputItemMetaChanged,
525                                   inputItemMetaChanged,
526                                   p_ext );
527             }
528
529             UnlockExtension( p_ext );
530             break;
531         }
532
533         default:
534             msg_Warn( p_mgr, "Control '%d' not yet implemented in Extension",
535                       i_control );
536             return VLC_EGENERIC;
537     }
538
539     return VLC_SUCCESS;
540 }
541
542 int lua_ExtensionActivate( extensions_manager_t *p_mgr, extension_t *p_ext )
543 {
544     assert( p_mgr != NULL && p_ext != NULL );
545     return lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END );
546 }
547
548 int lua_ExtensionDeactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
549 {
550     assert( p_mgr != NULL && p_ext != NULL );
551
552     if( !p_ext->p_sys->L )
553         return VLC_SUCCESS;
554
555     // Unset and release input objects
556     if( p_ext->p_sys->p_input )
557     {
558         if( p_ext->p_sys->i_capabilities & EXT_META_LISTENER )
559         {
560             // Release item
561             input_item_t *p_item = input_GetItem( p_ext->p_sys->p_input );
562             vlc_gc_decref( p_item );
563         }
564         vlc_object_release( p_ext->p_sys->p_input );
565     }
566
567     int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "deactivate", LUA_END );
568
569     /* Clear Lua State */
570     lua_close( p_ext->p_sys->L );
571     p_ext->p_sys->L = NULL;
572
573     return i_ret;
574 }
575
576 int lua_ExtensionWidgetClick( extensions_manager_t *p_mgr,
577                               extension_t *p_ext,
578                               extension_widget_t *p_widget )
579 {
580     if( !p_ext->p_sys->L )
581         return VLC_SUCCESS;
582
583     return lua_ExecuteFunction( p_mgr, p_ext, (const char*) p_widget->p_sys, LUA_END );
584 }
585
586
587 /**
588  * Get the list of menu entries from an extension script
589  * @param p_mgr
590  * @param p_ext
591  * @param pppsz_titles Pointer to NULL. All strings must be freed by the caller
592  * @param ppi_ids Pointer to NULL. Must be feed by the caller.
593  * @note This function is allowed to run in the UI thread. This means
594  *       that it MUST respond very fast.
595  * @todo Remove the menu() hook and provide a new function vlc.set_menu()
596  **/
597 static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
598                     char ***pppsz_titles, uint16_t **ppi_ids )
599 {
600     assert( *pppsz_titles == NULL );
601     assert( *ppi_ids == NULL );
602
603     if( !IsActivated( p_mgr, p_ext ) )
604     {
605         msg_Dbg( p_mgr, "Can't get menu before activating the extension!" );
606         return VLC_EGENERIC;
607     }
608
609     if( !LockExtension( p_ext ) )
610     {
611         /* Dying extension, fail. */
612         return VLC_EGENERIC;
613     }
614
615     int i_ret = VLC_EGENERIC;
616     lua_State *L = GetLuaState( p_mgr, p_ext );
617
618     if( ( p_ext->p_sys->i_capabilities & EXT_HAS_MENU ) == 0 )
619     {
620         msg_Dbg( p_mgr, "can't get a menu from an extension without menu!" );
621         goto exit;
622     }
623
624     lua_getglobal( L, "menu" );
625
626     if( !lua_isfunction( L, -1 ) )
627     {
628         msg_Warn( p_mgr, "Error while runing script %s, "
629                   "function menu() not found", p_ext->psz_name );
630         goto exit;
631     }
632
633     if( lua_pcall( L, 0, 1, 0 ) )
634     {
635         msg_Warn( p_mgr, "Error while runing script %s, "
636                   "function menu(): %s", p_ext->psz_name,
637                   lua_tostring( L, lua_gettop( L ) ) );
638         goto exit;
639     }
640
641     if( lua_gettop( L ) )
642     {
643         if( lua_istable( L, -1 ) )
644         {
645             /* Get table size */
646             size_t i_size = lua_objlen( L, -1 );
647             *pppsz_titles = ( char** ) calloc( i_size+1, sizeof( char* ) );
648             *ppi_ids = ( uint16_t* ) calloc( i_size+1, sizeof( uint16_t ) );
649
650             /* Walk table */
651             size_t i_idx = 0;
652             lua_pushnil( L );
653             while( lua_next( L, -2 ) != 0 )
654             {
655                 assert( i_idx < i_size );
656                 if( (!lua_isstring( L, -1 )) || (!lua_isnumber( L, -2 )) )
657                 {
658                     msg_Warn( p_mgr, "In script %s, an entry in "
659                               "the menu table is invalid!", p_ext->psz_name );
660                     goto exit;
661                 }
662                 (*pppsz_titles)[ i_idx ] = strdup( luaL_checkstring( L, -1 ) );
663                 (*ppi_ids)[ i_idx ] = (uint16_t) ( luaL_checkinteger( L, -2 ) & 0xFFFF );
664                 i_idx++;
665                 lua_pop( L, 1 );
666             }
667         }
668         else
669         {
670             msg_Warn( p_mgr, "Function menu() in script %s "
671                       "did not return a table", p_ext->psz_name );
672             goto exit;
673         }
674     }
675     else
676     {
677         msg_Warn( p_mgr, "Script %s went completely foobar", p_ext->psz_name );
678         goto exit;
679     }
680
681     i_ret = VLC_SUCCESS;
682
683 exit:
684     UnlockExtension( p_ext );
685     if( i_ret != VLC_SUCCESS )
686     {
687         msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
688                  __func__, __FILE__, __LINE__ );
689     }
690     return i_ret;
691 }
692
693 /* Must be entered with the Lock on Extension */
694 static lua_State* GetLuaState( extensions_manager_t *p_mgr,
695                                extension_t *p_ext )
696 {
697     lua_State *L = NULL;
698     if( p_ext )
699         L = p_ext->p_sys->L;
700
701     if( !L )
702     {
703         L = luaL_newstate();
704         if( !L )
705         {
706             msg_Err( p_mgr, "Could not create new Lua State" );
707             return NULL;
708         }
709         luaL_openlibs( L );
710         luaL_register( L, "vlc", p_reg );
711         luaopen_msg( L );
712
713         lua_pushlightuserdata( L, p_mgr );
714         lua_setfield( L, -2, "private" );
715
716         lua_pushlightuserdata( L, p_ext );
717         lua_setfield( L, -2, "extension" );
718
719         if( p_ext )
720         {
721             /* Load more libraries */
722             luaopen_acl( L );
723             luaopen_config( L );
724             luaopen_dialog( L, p_ext );
725             luaopen_input( L );
726             luaopen_msg( L );
727             luaopen_misc( L );
728             luaopen_net( L );
729             luaopen_object( L );
730             luaopen_osd( L );
731             luaopen_playlist( L );
732             luaopen_sd( L );
733             luaopen_stream( L );
734             luaopen_strings( L );
735             luaopen_variables( L );
736             luaopen_video( L );
737             luaopen_vlm( L );
738             luaopen_volume( L );
739             luaopen_xml( L );
740
741             /* Register extension specific functions */
742             lua_getglobal( L, "vlc" );
743             lua_pushcfunction( L, vlclua_extension_deactivate );
744             lua_setfield( L, -2, "deactivate" );
745
746             /* Setup the module search path */
747             if( vlclua_add_modules_path( p_mgr, L, p_ext->psz_name ) )
748             {
749                 msg_Warn( p_mgr, "Error while setting the module search path for %s", p_ext->psz_name );
750                 return NULL;
751             }
752
753             /* Load and run the script(s) */
754             if( luaL_dofile( L, p_ext->psz_name ) != 0 )
755             {
756                 msg_Warn( p_mgr, "Error loading script %s: %s", p_ext->psz_name,
757                           lua_tostring( L, lua_gettop( L ) ) );
758                 lua_pop( L, 1 );
759                 return NULL;
760             }
761
762             p_ext->p_sys->L = L;
763         }
764     }
765 #ifndef NDEBUG
766     else
767     {
768         msg_Dbg( p_mgr, "Reusing old Lua state for extension '%s'",
769                  p_ext->psz_name );
770     }
771 #endif
772
773     return L;
774 }
775
776 int lua_ExecuteFunction( extensions_manager_t *p_mgr, extension_t *p_ext,
777                             const char *psz_function, ... )
778 {
779     va_list args;
780     va_start( args, psz_function );
781     int i_ret = lua_ExecuteFunctionVa( p_mgr, p_ext, psz_function, args );
782     va_end( args );
783     return i_ret;
784 }
785
786 /**
787  * Execute a function in a Lua script
788  * @return < 0 in case of failure, >= 0 in case of success
789  * @note It's better to call this function from a dedicated thread
790  * (see extension_thread.c)
791  **/
792 int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext,
793                             const char *psz_function, va_list args )
794 {
795     int i_ret = VLC_EGENERIC;
796     int i_args = 0;
797     assert( p_mgr != NULL );
798     assert( p_ext != NULL );
799
800     lua_State *L = GetLuaState( p_mgr, p_ext );
801     lua_getglobal( L, psz_function );
802
803     if( !lua_isfunction( L, -1 ) )
804     {
805         msg_Warn( p_mgr, "Error while runing script %s, "
806                   "function %s() not found", p_ext->psz_name, psz_function );
807         goto exit;
808     }
809
810     lua_datatype_e type = LUA_END;
811     while( ( type = va_arg( args, int ) ) != LUA_END )
812     {
813         if( type == LUA_NUM )
814         {
815             lua_pushnumber( L , ( int ) va_arg( args, int ) );
816         }
817         else if( type == LUA_TEXT )
818         {
819             lua_pushstring( L , ( char * ) va_arg( args, char* ) );
820         }
821         else
822         {
823             msg_Warn( p_mgr, "Undefined argument type %d to lua function %s"
824                    "from script %s", type, psz_function, p_ext->psz_name );
825             goto exit;
826         }
827         i_args ++;
828     }
829     if( lua_pcall( L, i_args, 1, 0 ) )
830     {
831         msg_Warn( p_mgr, "Error while runing script %s, "
832                   "function %s(): %s", p_ext->psz_name, psz_function,
833                   lua_tostring( L, lua_gettop( L ) ) );
834         goto exit;
835     }
836
837     i_ret = lua_DialogFlush( L );
838 exit:
839     return i_ret;
840
841 }
842
843 static inline int TriggerMenu( extension_t *p_ext, int i_id )
844 {
845     return PushCommand( p_ext, CMD_TRIGGERMENU, i_id );
846 }
847
848 int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
849                               extension_t *p_ext, int id )
850 {
851     int i_ret = VLC_EGENERIC;
852     lua_State *L = GetLuaState( p_mgr, p_ext );
853
854     if( !L )
855         return VLC_EGENERIC;
856
857     luaopen_dialog( L, p_ext );
858
859     lua_getglobal( L, "trigger_menu" );
860     if( !lua_isfunction( L, -1 ) )
861     {
862         msg_Warn( p_mgr, "Error while runing script %s, "
863                   "function trigger_menu() not found", p_ext->psz_name );
864         return VLC_EGENERIC;
865     }
866
867     /* Pass id as unique argument to the function */
868     lua_pushinteger( L, id );
869
870     if( lua_pcall( L, 1, 1, 0 ) != 0 )
871     {
872         msg_Warn( p_mgr, "Error while runing script %s, "
873                   "function trigger_menu(): %s", p_ext->psz_name,
874                   lua_tostring( L, lua_gettop( L ) ) );
875         return VLC_EGENERIC;
876     }
877
878     i_ret = lua_DialogFlush( L );
879     if( i_ret < VLC_SUCCESS )
880     {
881         msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
882                  __func__, __FILE__, __LINE__ );
883     }
884     return i_ret;
885 }
886
887 /** Directly trigger an extension, without activating it
888  * This is NOT multithreaded, and this code runs in the UI thread
889  * @param p_mgr
890  * @param p_ext Extension to trigger
891  * @return Value returned by the lua function "trigger"
892  **/
893 static int TriggerExtension( extensions_manager_t *p_mgr,
894                              extension_t *p_ext )
895 {
896     int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "trigger", LUA_END );
897
898     /* Close lua state for trigger-only extensions */
899     if( p_ext->p_sys->L )
900         lua_close( p_ext->p_sys->L );
901     p_ext->p_sys->L = NULL;
902
903     return i_ret;
904 }
905
906 /** Retrieve extension associated to the current script
907  * @param L current lua_State
908  * @return Lua userdata "vlc.extension"
909  **/
910 extension_t *vlclua_extension_get( lua_State *L )
911 {
912     extension_t *p_ext = NULL;
913     lua_getglobal( L, "vlc" );
914     lua_getfield( L, -1, "extension" );
915     p_ext = (extension_t*) lua_topointer( L, lua_gettop( L ) );
916     lua_pop( L, 2 );
917     return p_ext;
918 }
919
920 /** Deactivate an extension by order from the extension itself
921  * @param L lua_State
922  * @note This is an asynchronous call. A script calling vlc.deactivate() will
923  * be executed to the end before the last call to deactivate() is done.
924  **/
925 int vlclua_extension_deactivate( lua_State *L )
926 {
927     extension_t *p_ext = vlclua_extension_get( L );
928     int i_ret = Deactivate( p_ext->p_sys->p_mgr, p_ext );
929     return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
930 }
931
932 /** Callback for the variable "dialog-event"
933  * @param p_this Current object owner of the extension and the dialog
934  * @param psz_var "dialog-event"
935  * @param oldval Unused
936  * @param newval Address of the dialog
937  * @param p_data Unused
938  **/
939 static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
940                                              char const *psz_var,
941                                              vlc_value_t oldval,
942                                              vlc_value_t newval,
943                                              void *p_data )
944 {
945     /* psz_var == "dialog-event" */
946     ( void ) psz_var;
947     ( void ) oldval;
948     ( void ) p_data;
949
950     extension_dialog_command_t *command = newval.p_address;
951     assert( command != NULL );
952     assert( command->p_dlg != NULL);
953
954     extension_t *p_ext = command->p_dlg->p_sys;
955     assert( p_ext != NULL );
956
957     extension_widget_t *p_widget = command->p_data;
958
959     switch( command->event )
960     {
961         case EXTENSION_EVENT_CLICK:
962             assert( p_widget != NULL );
963             PushCommand( p_ext, CMD_CLICK, p_widget );
964             break;
965         case EXTENSION_EVENT_CLOSE:
966             PushCommandUnique( p_ext, CMD_CLOSE );
967             break;
968         default:
969             msg_Dbg( p_this, "Received unknown UI event %d, discarded",
970                      command->event );
971             break;
972     }
973
974     return VLC_SUCCESS;
975 }
976
977 /** Callback on vlc_InputItemMetaChanged event
978  **/
979 static void inputItemMetaChanged( const vlc_event_t *p_event,
980                                   void *data )
981 {
982     assert( p_event && p_event->type == vlc_InputItemMetaChanged );
983
984     extension_t *p_ext = ( extension_t* ) data;
985     assert( p_ext != NULL );
986
987     PushCommandUnique( p_ext, CMD_UPDATE_META );
988 }
989
990 /* Lock this extension. Can fail. */
991 bool LockExtension( extension_t *p_ext )
992 {
993     if( p_ext->p_sys->b_exiting )
994         return false;
995
996     vlc_mutex_lock( &p_ext->p_sys->running_lock );
997     if( p_ext->p_sys->b_exiting )
998     {
999         vlc_mutex_unlock( &p_ext->p_sys->running_lock );
1000         return false;
1001     }
1002
1003     return true;
1004 }
1005
1006 void UnlockExtension( extension_t *p_ext )
1007 {
1008     vlc_mutex_unlock( &p_ext->p_sys->running_lock );
1009 }