]> git.sesse.net Git - vlc/blob - modules/misc/lua/extension.c
lua/extension: Missing lock initialization.
[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 /* Functions to register */
30 static const luaL_Reg p_reg[] =
31 {
32     { NULL, NULL }
33 };
34
35 /*
36  * Extensions capabilities
37  * Note: #define and ppsz_capabilities must be in sync
38  */
39 #define EXT_HAS_MENU          (1 << 0)
40 #define EXT_TRIGGER_ONLY      (1 << 1)
41
42 const char* const ppsz_capabilities[] = {
43     "menu",
44     "trigger",
45     NULL
46 };
47
48 static int ScanExtensions( extensions_manager_t *p_this );
49 static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
50                             lua_State *L, void *pb_continue );
51 static int Control( extensions_manager_t *, int, va_list );
52 static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
53                     char ***pppsz_titles, uint16_t **ppi_ids );
54 static lua_State* GetLuaState( extensions_manager_t *p_mgr,
55                                extension_t *p_ext );
56 static int TriggerMenu( extension_t *p_ext, int id );
57 static int TriggerExtension( extensions_manager_t *p_mgr,
58                              extension_t *p_ext );
59
60 int vlclua_extension_deactivate( lua_State *L );
61
62 /* Interactions */
63 static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
64                                              char const *psz_var,
65                                              vlc_value_t oldval,
66                                              vlc_value_t newval,
67                                              void *p_data );
68
69
70 /**
71  * Module entry-point
72  **/
73 int Open_Extension( vlc_object_t *p_this )
74 {
75     msg_Dbg( p_this, "Opening EXPERIMENTAL Lua Extension module" );
76
77     extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
78
79     p_mgr->pf_control = Control;
80
81     extensions_manager_sys_t *p_sys = ( extensions_manager_sys_t* )
82                     calloc( 1, sizeof( extensions_manager_sys_t ) );
83     if( !p_sys ) return VLC_ENOMEM;
84
85     p_mgr->p_sys = p_sys;
86     ARRAY_INIT( p_sys->activated_extensions );
87     ARRAY_INIT( p_mgr->extensions );
88     vlc_mutex_init( &p_mgr->p_sys->lock );
89
90     /* Initialise Lua state structure */
91     lua_State *L = GetLuaState( p_mgr, NULL );
92     if( !L )
93     {
94         free( p_sys );
95         return VLC_EGENERIC;
96     }
97     p_sys->L = L;
98
99     /* Scan available Lua Extensions */
100     if( ScanExtensions( p_mgr ) != VLC_SUCCESS )
101     {
102         msg_Err( p_mgr, "Can't load extensions modules" );
103         return VLC_EGENERIC;
104     }
105
106     lua_close( L );
107     p_sys->L = NULL;
108
109     vlc_mutex_init( &p_sys->lock );
110
111     // Create the dialog-event variable
112     var_Create( p_this, "dialog-event", VLC_VAR_ADDRESS );
113     var_AddCallback( p_this, "dialog-event",
114                      vlclua_extension_dialog_callback, NULL );
115
116     return VLC_SUCCESS;
117 }
118
119 /**
120  * Module unload function
121  **/
122 void Close_Extension( vlc_object_t *p_this )
123 {
124     extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
125     msg_Dbg( p_mgr, "Deactivating all loaded extensions" );
126
127     vlc_mutex_lock( &p_mgr->p_sys->lock );
128     p_mgr->p_sys->b_killed = true;
129     vlc_mutex_unlock( &p_mgr->p_sys->lock );
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         Deactivate( p_mgr, p_ext );
136         WaitForDeactivation( p_ext );
137     }
138     FOREACH_END()
139
140     msg_Dbg( p_mgr, "All extensions are now deactivated" );
141     ARRAY_RESET( p_mgr->p_sys->activated_extensions );
142
143     if( p_mgr->p_sys && p_mgr->p_sys->L )
144         lua_close( p_mgr->p_sys->L );
145
146     vlc_mutex_destroy( &p_mgr->p_sys->lock );
147     free( p_mgr->p_sys );
148     p_mgr->p_sys = NULL;
149
150     /* Free extensions' memory */
151     FOREACH_ARRAY( p_ext, p_mgr->extensions )
152     {
153         if( !p_ext )
154             break;
155         if( p_ext->p_sys->L )
156             lua_close( p_ext->p_sys->L );
157         free( p_ext->psz_name );
158         free( p_ext->psz_title );
159
160         vlc_mutex_destroy( &p_ext->p_sys->running_lock );
161         vlc_mutex_destroy( &p_ext->p_sys->command_lock );
162         vlc_cond_destroy( &p_ext->p_sys->wait );
163
164         free( p_ext->p_sys );
165         free( p_ext );
166     }
167     FOREACH_END()
168
169     ARRAY_RESET( p_mgr->extensions );
170
171     var_Destroy( p_mgr, "dialog-event" );
172 }
173
174 /**
175  * Batch scan all Lua files in folder "extensions"
176  * @param p_mgr This extensions_manager_t object
177  **/
178 static int ScanExtensions( extensions_manager_t *p_mgr )
179 {
180     bool b_true = true;
181     int i_ret =
182         vlclua_scripts_batch_execute( VLC_OBJECT( p_mgr ),
183                                       "extensions",
184                                       &ScanLuaCallback,
185                                       p_mgr->p_sys->L, &b_true );
186
187     if( !i_ret )
188         return VLC_EGENERIC;
189
190     return VLC_SUCCESS;
191 }
192
193 /**
194  * Batch scan all Lua files in folder "extensions": callback
195  * @param p_this This extensions_manager_t object
196  * @param psz_script Name of the script to run
197  * @param L Lua State, common to all scripts here
198  * @param pb_continue bool* that indicates whether to continue batch or not
199  **/
200 int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
201                      lua_State *L, void *pb_continue )
202 {
203     extensions_manager_t *p_mgr = ( extensions_manager_t* ) p_this;
204     bool b_ok = false;
205
206     msg_Dbg( p_mgr, "Scanning Lua script %s", psz_script );
207
208     vlc_mutex_lock( &p_mgr->p_sys->lock );
209
210     /* Create new script descriptor */
211     extension_t *p_ext = ( extension_t* ) calloc( 1, sizeof( extension_t ) );
212     if( !p_ext )
213     {
214         vlc_mutex_unlock( &p_mgr->p_sys->lock );
215         return 0;
216     }
217
218     p_ext->psz_name = strdup( psz_script );
219     p_ext->p_sys = (extension_sys_t*) calloc( 1, sizeof( extension_sys_t ) );
220     if( !p_ext->p_sys || !p_ext->psz_name )
221     {
222         free( p_ext->psz_name );
223         free( p_ext->p_sys );
224         free( p_ext );
225         vlc_mutex_unlock( &p_mgr->p_sys->lock );
226         return 0;
227     }
228     p_ext->p_sys->p_mgr = p_mgr;
229
230     /* Mutexes and conditions */
231     vlc_mutex_init( &p_ext->p_sys->command_lock );
232     vlc_mutex_init( &p_ext->p_sys->running_lock );
233     vlc_cond_init( &p_ext->p_sys->wait );
234
235     /* Load and run the script(s) */
236     if( luaL_dofile( L, psz_script ) )
237     {
238         msg_Warn( p_mgr, "Error loading script %s: %s", psz_script,
239                   lua_tostring( L, lua_gettop( L ) ) );
240         lua_pop( L, 1 );
241         goto exit;
242     }
243
244     /* Scan script for capabilities */
245     lua_getglobal( L, "descriptor" );
246
247     if( !lua_isfunction( L, -1 ) )
248     {
249         msg_Warn( p_mgr, "Error while runing script %s, "
250                   "function descriptor() not found", psz_script );
251         goto exit;
252     }
253
254     if( lua_pcall( L, 0, 1, 0 ) )
255     {
256         msg_Warn( p_mgr, "Error while runing script %s, "
257                   "function descriptor(): %s", psz_script,
258                   lua_tostring( L, lua_gettop( L ) ) );
259         goto exit;
260     }
261
262     if( lua_gettop( L ) )
263     {
264         if( lua_istable( L, -1 ) )
265         {
266             lua_getfield( L, -1, "capabilities" );
267             if( lua_istable( L, -1 ) )
268             {
269                 lua_pushnil( L );
270                 while( lua_next( L, -2 ) != 0 )
271                 {
272                     /* Key is at index -2 and value at index -1. Discard key */
273                     const char *psz_cap = luaL_checkstring( L, -1 );
274                     int i_cap = 0;
275                     bool b_ok = false;
276                     /* Find this capability's flag */
277                     for( const char *iter = *ppsz_capabilities;
278                          iter != NULL;
279                          iter = ppsz_capabilities[ ++i_cap ])
280                     {
281                         if( !strcmp( iter, psz_cap ) )
282                         {
283                             /* Flag it! */
284                             p_ext->p_sys->i_capabilities |= 1 << i_cap;
285                             b_ok = true;
286                             break;
287                         }
288                     }
289                     if( !b_ok )
290                     {
291                         msg_Warn( p_mgr, "Extension capability '%s' unknown in"
292                                   " script %s", psz_cap, psz_script );
293                     }
294                     /* Removes 'value'; keeps 'key' for next iteration */
295                     lua_pop( L, 1 );
296                 }
297             }
298             else
299             {
300                 msg_Warn( p_mgr, "In script %s, function descriptor() "
301                               "did not return a table of capabilities.",
302                               psz_script );
303             }
304
305             lua_pop( L, 1 );
306             lua_getfield( L, -1, "title" );
307             if( lua_isstring( L, -1 ) )
308             {
309                 p_ext->psz_title = strdup( luaL_checkstring( L, -1 ) );
310             }
311             else
312             {
313                 msg_Dbg( p_mgr, "In script %s, function descriptor() "
314                                 "did not return a string as title.",
315                                 psz_script );
316                 p_ext->psz_title = strdup( psz_script );
317             }
318         }
319         else
320         {
321             msg_Warn( p_mgr, "In script %s, function descriptor() "
322                       "did not return a table!", psz_script );
323             goto exit;
324         }
325     }
326     else
327     {
328         msg_Err( p_mgr, "Script %s went completely foobar", psz_script );
329         goto exit;
330     }
331
332     msg_Dbg( p_mgr, "Script %s has the following capability flags: 0x%x",
333              psz_script, p_ext->p_sys->i_capabilities );
334
335     b_ok = true;
336 exit:
337     if( !b_ok )
338     {
339         free( p_ext->psz_name );
340         free( p_ext->psz_title );
341         vlc_mutex_destroy( &p_ext->p_sys->command_lock );
342         vlc_mutex_destroy( &p_ext->p_sys->running_lock );
343         vlc_cond_destroy( &p_ext->p_sys->wait );
344         free( p_ext->p_sys );
345         free( p_ext );
346     }
347     else
348     {
349         /* Add the extension to the list of known extensions */
350         ARRAY_APPEND( p_mgr->extensions, p_ext );
351     }
352
353     vlc_mutex_unlock( &p_mgr->p_sys->lock );
354     /* Continue batch execution */
355     return pb_continue ? ( (* (bool*)pb_continue) ? -1 : 0 ) : -1;
356 }
357
358 static int Control( extensions_manager_t *p_mgr, int i_control, va_list args )
359 {
360     extension_t *p_ext = NULL;
361     bool *pb = NULL;
362     uint16_t **ppus = NULL;
363     char ***pppsz = NULL;
364     int i = 0;
365
366     switch( i_control )
367     {
368         case EXTENSION_ACTIVATE:
369             p_ext = ( extension_t* ) va_arg( args, extension_t* );
370             return Activate( p_mgr, p_ext );
371
372         case EXTENSION_DEACTIVATE:
373             p_ext = ( extension_t* ) va_arg( args, extension_t* );
374             return Deactivate( p_mgr, p_ext );
375
376         case EXTENSION_IS_ACTIVATED:
377             p_ext = ( extension_t* ) va_arg( args, extension_t* );
378             pb = ( bool* ) va_arg( args, bool* );
379             *pb = IsActivated( p_mgr, p_ext );
380             break;
381
382         case EXTENSION_HAS_MENU:
383             p_ext = ( extension_t* ) va_arg( args, extension_t* );
384             pb = ( bool* ) va_arg( args, bool* );
385             *pb = ( p_ext->p_sys->i_capabilities & EXT_HAS_MENU ) ? 1 : 0;
386             break;
387
388         case EXTENSION_GET_MENU:
389             p_ext = ( extension_t* ) va_arg( args, extension_t* );
390             pppsz = ( char*** ) va_arg( args, char*** );
391             ppus = ( uint16_t** ) va_arg( args, uint16_t** );
392             return GetMenuEntries( p_mgr, p_ext, pppsz, ppus );
393
394         case EXTENSION_TRIGGER_ONLY:
395             p_ext = ( extension_t* ) va_arg( args, extension_t* );
396             pb = ( bool* ) va_arg( args, bool* );
397             *pb = ( p_ext->p_sys->i_capabilities & EXT_TRIGGER_ONLY ) ? 1 : 0;
398             break;
399
400         case EXTENSION_TRIGGER:
401             p_ext = ( extension_t* ) va_arg( args, extension_t* );
402             return TriggerExtension( p_mgr, p_ext );
403
404         case EXTENSION_TRIGGER_MENU:
405             p_ext = ( extension_t* ) va_arg( args, extension_t* );
406             // GCC: 'uint16_t' is promoted to 'int' when passed through '...'
407             i = ( int ) va_arg( args, int );
408             return TriggerMenu( p_ext, i );
409
410         default:
411             msg_Err( p_mgr, "Control '%d' not yet implemented in Extension",
412                      i_control );
413             return VLC_EGENERIC;
414     }
415
416     return VLC_SUCCESS;
417 }
418
419 int lua_ExtensionActivate( extensions_manager_t *p_mgr, extension_t *p_ext )
420 {
421     assert( p_mgr != NULL && p_ext != NULL );
422     return lua_ExecuteFunction( p_mgr, p_ext, "activate" );
423 }
424
425 int lua_ExtensionDeactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
426 {
427     assert( p_mgr != NULL && p_ext != NULL );
428
429     if( !p_ext->p_sys->L )
430         return VLC_SUCCESS;
431
432     int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "deactivate" );
433
434     /* Clear Lua State */
435     lua_close( p_ext->p_sys->L );
436     p_ext->p_sys->L = NULL;
437
438     return i_ret;
439 }
440
441 int lua_ExtensionWidgetClick( extensions_manager_t *p_mgr,
442                               extension_t *p_ext,
443                               extension_widget_t *p_widget )
444 {
445     if( !p_ext->p_sys->L )
446         return VLC_SUCCESS;
447
448     return lua_ExecuteFunction( p_mgr, p_ext, (const char*) p_widget->p_sys );
449 }
450
451
452 /**
453  * Get the list of menu entries from an extension script
454  * @param p_mgr
455  * @param p_ext
456  * @param pppsz_titles Pointer to NULL. All strings must be freed by the caller
457  * @param ppi_ids Pointer to NULL. Must be feed by the caller.
458  * @note This function is allowed to run in the UI thread. This means
459  *       that it MUST respond very fast.
460  * @todo Remove the menu() hook and provide a new function vlc.set_menu()
461  **/
462 static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
463                     char ***pppsz_titles, uint16_t **ppi_ids )
464 {
465     assert( *pppsz_titles == NULL );
466     assert( *ppi_ids == NULL );
467
468     if( !IsActivated( p_mgr, p_ext ) )
469     {
470         msg_Dbg( p_mgr, "Can't get menu before activating the extension!" );
471         return VLC_EGENERIC;
472     }
473
474     if( !LockExtension( p_ext ) )
475     {
476         /* Dying extension, fail. */
477         return VLC_EGENERIC;
478     }
479
480     int i_ret = VLC_EGENERIC;
481     lua_State *L = GetLuaState( p_mgr, p_ext );
482
483     if( ( p_ext->p_sys->i_capabilities & EXT_HAS_MENU ) == 0 )
484     {
485         msg_Dbg( p_mgr, "can't get a menu from an extension without menu!" );
486         goto exit;
487     }
488
489     lua_getglobal( L, "menu" );
490
491     if( !lua_isfunction( L, -1 ) )
492     {
493         msg_Warn( p_mgr, "Error while runing script %s, "
494                   "function menu() not found", p_ext->psz_name );
495         goto exit;
496     }
497
498     if( lua_pcall( L, 0, 1, 0 ) )
499     {
500         msg_Warn( p_mgr, "Error while runing script %s, "
501                   "function menu(): %s", p_ext->psz_name,
502                   lua_tostring( L, lua_gettop( L ) ) );
503         goto exit;
504     }
505
506     if( lua_gettop( L ) )
507     {
508         if( lua_istable( L, -1 ) )
509         {
510             /* Get table size */
511             size_t i_size = lua_objlen( L, -1 );
512             *pppsz_titles = ( char** ) calloc( i_size+1, sizeof( char* ) );
513             *ppi_ids = ( uint16_t* ) calloc( i_size+1, sizeof( uint16_t ) );
514
515             /* Walk table */
516             size_t i_idx = 0;
517             lua_pushnil( L );
518             while( lua_next( L, -2 ) != 0 )
519             {
520                 assert( i_idx < i_size );
521                 if( (!lua_isstring( L, -1 )) || (!lua_isnumber( L, -2 )) )
522                 {
523                     msg_Warn( p_mgr, "In script %s, an entry in "
524                               "the menu table is invalid!", p_ext->psz_name );
525                     goto exit;
526                 }
527                 (*pppsz_titles)[ i_idx ] = strdup( luaL_checkstring( L, -1 ) );
528                 (*ppi_ids)[ i_idx ] = (uint16_t) ( luaL_checkinteger( L, -2 ) & 0xFFFF );
529                 i_idx++;
530                 lua_pop( L, 1 );
531             }
532         }
533         else
534         {
535             msg_Warn( p_mgr, "Function menu() in script %s "
536                       "did not return a table", p_ext->psz_name );
537             goto exit;
538         }
539     }
540     else
541     {
542         msg_Warn( p_mgr, "Script %s went completely foobar", p_ext->psz_name );
543         goto exit;
544     }
545
546     i_ret = VLC_SUCCESS;
547
548 exit:
549     UnlockExtension( p_ext );
550     if( i_ret != VLC_SUCCESS )
551     {
552         msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
553                  __func__, __FILE__, __LINE__ );
554     }
555     return i_ret;
556 }
557
558 /* Must be entered with the Lock on Extension */
559 static lua_State* GetLuaState( extensions_manager_t *p_mgr,
560                                extension_t *p_ext )
561 {
562     lua_State *L = NULL;
563     if( p_ext )
564         L = p_ext->p_sys->L;
565
566     if( !L )
567     {
568         L = luaL_newstate();
569         if( !L )
570         {
571             msg_Err( p_mgr, "Could not create new Lua State" );
572             return NULL;
573         }
574         luaL_openlibs( L );
575         luaL_register( L, "vlc", p_reg );
576         luaopen_msg( L );
577
578         lua_pushlightuserdata( L, p_mgr );
579         lua_setfield( L, -2, "private" );
580
581         lua_pushlightuserdata( L, p_ext );
582         lua_setfield( L, -2, "extension" );
583
584         if( p_ext )
585         {
586             /* Load more libraries */
587             luaopen_acl( L );
588             luaopen_config( L );
589             luaopen_dialog( L, p_ext );
590             luaopen_input( L );
591             luaopen_msg( L );
592             luaopen_misc( L );
593             luaopen_net( L );
594             luaopen_object( L );
595             luaopen_osd( L );
596             luaopen_playlist( L );
597             luaopen_sd( L );
598             luaopen_stream( L );
599             luaopen_strings( L );
600             luaopen_variables( L );
601             luaopen_video( L );
602             luaopen_vlm( L );
603             luaopen_volume( L );
604
605             /* Register extension specific functions */
606             lua_getglobal( L, "vlc" );
607             lua_pushcfunction( L, vlclua_extension_deactivate );
608             lua_setfield( L, -2, "deactivate" );
609
610             /* Load and run the script(s) */
611             if( luaL_dofile( L, p_ext->psz_name ) != 0 )
612             {
613                 msg_Warn( p_mgr, "Error loading script %s: %s", p_ext->psz_name,
614                           lua_tostring( L, lua_gettop( L ) ) );
615                 lua_pop( L, 1 );
616                 return NULL;
617             }
618
619             p_ext->p_sys->L = L;
620         }
621     }
622 #ifndef NDEBUG
623     else
624     {
625         msg_Dbg( p_mgr, "Reusing old Lua state for extension '%s'",
626                  p_ext->psz_name );
627     }
628 #endif
629
630     return L;
631 }
632
633 /**
634  * Execute a function in a Lua script
635  * @return < 0 in case of failure, >= 0 in case of success
636  * @note It's better to call this function from a dedicated thread
637  * (see extension_thread.c)
638  **/
639 int lua_ExecuteFunction( extensions_manager_t *p_mgr,
640                          extension_t *p_ext,
641                          const char *psz_function )
642 {
643     int i_ret = VLC_EGENERIC;
644     assert( p_mgr != NULL );
645     assert( p_ext != NULL );
646
647     lua_State *L = GetLuaState( p_mgr, p_ext );
648     lua_getglobal( L, psz_function );
649
650     if( !lua_isfunction( L, -1 ) )
651     {
652         msg_Warn( p_mgr, "Error while runing script %s, "
653                   "function %s() not found", p_ext->psz_name, psz_function );
654         goto exit;
655     }
656
657     if( lua_pcall( L, 0, 1, 0 ) )
658     {
659         msg_Warn( p_mgr, "Error while runing script %s, "
660                   "function %s(): %s", p_ext->psz_name, psz_function,
661                   lua_tostring( L, lua_gettop( L ) ) );
662         goto exit;
663     }
664
665     i_ret = VLC_SUCCESS;
666 exit:
667     return i_ret;
668 }
669
670 static inline int TriggerMenu( extension_t *p_ext, int i_id )
671 {
672     return PushCommand( p_ext, CMD_TRIGGERMENU, i_id );
673 }
674
675 int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
676                               extension_t *p_ext, int id )
677 {
678     int i_ret = VLC_EGENERIC;
679     lua_State *L = GetLuaState( p_mgr, p_ext );
680
681     if( !L )
682         return VLC_EGENERIC;
683
684     luaopen_dialog( L, p_ext );
685
686     lua_getglobal( L, "trigger_menu" );
687     if( !lua_isfunction( L, -1 ) )
688     {
689         msg_Warn( p_mgr, "Error while runing script %s, "
690                   "function trigger_menu() not found", p_ext->psz_name );
691         return VLC_EGENERIC;
692     }
693
694     /* Pass id as unique argument to the function */
695     lua_pushinteger( L, id );
696
697     if( lua_pcall( L, 1, 1, 0 ) != 0 )
698     {
699         msg_Warn( p_mgr, "Error while runing script %s, "
700                   "function trigger_menu(): %s", p_ext->psz_name,
701                   lua_tostring( L, lua_gettop( L ) ) );
702         return VLC_EGENERIC;
703     }
704
705     if( i_ret < VLC_SUCCESS )
706     {
707         msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
708                  __func__, __FILE__, __LINE__ );
709     }
710     return i_ret;
711 }
712
713 /** Directly trigger an extension, without activating it
714  * This is NOT multithreaded, and this code runs in the UI thread
715  * @param p_mgr
716  * @param p_ext Extension to trigger
717  * @return Value returned by the lua function "trigger"
718  **/
719 static int TriggerExtension( extensions_manager_t *p_mgr,
720                              extension_t *p_ext )
721 {
722     int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "trigger" );
723
724     /* Close lua state for trigger-only extensions */
725     if( p_ext->p_sys->L )
726         lua_close( p_ext->p_sys->L );
727     p_ext->p_sys->L = NULL;
728
729     return i_ret;
730 }
731
732 /** Retrieve extension associated to the current script
733  * @param L current lua_State
734  * @return Lua userdata "vlc.extension"
735  **/
736 extension_t *vlclua_extension_get( lua_State *L )
737 {
738     extension_t *p_ext = NULL;
739     lua_getglobal( L, "vlc" );
740     lua_getfield( L, -1, "extension" );
741     p_ext = (extension_t*) lua_topointer( L, lua_gettop( L ) );
742     lua_pop( L, 2 );
743     return p_ext;
744 }
745
746 /** Deactivate an extension by order from the extension itself
747  * @param L lua_State
748  * @note This is an asynchronous call. A script calling vlc.deactivate() will
749  * be executed to the end before the last call to deactivate() is done.
750  **/
751 int vlclua_extension_deactivate( lua_State *L )
752 {
753     extension_t *p_ext = vlclua_extension_get( L );
754     int i_ret = Deactivate( p_ext->p_sys->p_mgr, p_ext );
755     return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
756 }
757
758 /** Callback for the variable "dialog-event"
759  * @param p_this Current object owner of the extension and the dialog
760  * @param psz_var "dialog-event"
761  * @param oldval Unused
762  * @param newval Address of the dialog
763  * @param p_data Unused
764  **/
765 static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
766                                              char const *psz_var,
767                                              vlc_value_t oldval,
768                                              vlc_value_t newval,
769                                              void *p_data )
770 {
771     /* psz_var == "dialog-event" */
772     ( void ) psz_var;
773     ( void ) oldval;
774     ( void ) p_data;
775
776     extension_dialog_command_t *command = newval.p_address;
777     assert( command != NULL );
778     assert( command->p_dlg != NULL);
779
780     extension_t *p_ext = command->p_dlg->p_sys;
781     assert( p_ext != NULL );
782
783     extension_widget_t *p_widget = command->p_data;
784
785     switch( command->event )
786     {
787         case EXTENSION_EVENT_CLICK:
788             assert( p_widget != NULL );
789             PushCommand( p_ext, CMD_CLICK, p_widget );
790             break;
791         case EXTENSION_EVENT_CLOSE:
792             PushCommand( p_ext, CMD_CLOSE );
793             break;
794         default:
795             msg_Dbg( p_this, "Received unknown UI event %d, discarded",
796                      command->event );
797             break;
798     }
799
800     return VLC_SUCCESS;
801 }
802
803 /* Lock this extension. Can fail. */
804 bool LockExtension( extension_t *p_ext )
805 {
806     if( p_ext->p_sys->b_exiting )
807         return false;
808
809     vlc_mutex_lock( &p_ext->p_sys->running_lock );
810     if( p_ext->p_sys->b_exiting )
811     {
812         vlc_mutex_unlock( &p_ext->p_sys->running_lock );
813         return false;
814     }
815
816     return true;
817 }
818
819 void UnlockExtension( extension_t *p_ext )
820 {
821     vlc_mutex_unlock( &p_ext->p_sys->running_lock );
822 }