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