]> git.sesse.net Git - vlc/blob - modules/misc/lua/extension_thread.c
Lua: Allow arguments to be passed to lua functions in lua_ExecuteFunction
[vlc] / modules / misc / lua / extension_thread.c
1 /*****************************************************************************
2  * extension_thread.c: Extensions Manager, Threads manager (no Lua here)
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 /* I don't want to include lua headers here */
25 typedef struct lua_State lua_State;
26
27 #include "extension.h"
28 #include "assert.h"
29
30 struct thread_sys_t
31 {
32     extensions_manager_t *p_mgr;
33     extension_t *p_ext;
34 };
35
36 /** Thread Run */
37 static void* Run( void *data );
38 static void FreeCommands( struct command_t *command );
39 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext );
40
41 /**
42  * Activate an extension
43  * @param p_mgr This manager
44  * @param p_ext Extension to activate
45  * @return The usual VLC return codes
46  **/
47 int Activate( extensions_manager_t *p_mgr, extension_t *p_ext )
48 {
49     assert( p_ext != NULL );
50
51     struct extension_sys_t *p_sys = p_ext->p_sys;
52     assert( p_sys != NULL );
53
54     msg_Dbg( p_mgr, "Activating extension '%s'", p_ext->psz_title );
55
56     if( IsActivated( p_mgr, p_ext ) )
57     {
58         msg_Warn( p_mgr, "Extension is already activated!" );
59         return VLC_EGENERIC;
60     }
61
62     /* Add this script to the activated extensions list */
63     vlc_mutex_lock( &p_mgr->p_sys->lock );
64     ARRAY_APPEND( p_mgr->p_sys->activated_extensions, p_ext );
65     vlc_mutex_unlock( &p_mgr->p_sys->lock );
66
67     /* Prepare first command */
68     p_sys->command = calloc( 1, sizeof( struct command_t ) );
69     if( !p_sys->command )
70         return VLC_ENOMEM;
71     p_sys->command->i_command = CMD_ACTIVATE; /* No params */
72
73     /* Start thread */
74     p_sys->b_exiting = false;
75
76     if( vlc_clone( &p_sys->thread, Run, p_ext, VLC_THREAD_PRIORITY_LOW )
77         != VLC_SUCCESS )
78     {
79         p_sys->b_exiting = true;
80         // Note: Automatically deactivating the extension...
81         Deactivate( p_mgr, p_ext );
82         return VLC_ENOMEM;
83     }
84
85     return VLC_SUCCESS;
86 }
87
88 /** Look for an extension in the activated extensions list */
89 bool IsActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
90 {
91     assert( p_ext != NULL );
92     vlc_mutex_lock( &p_mgr->p_sys->lock );
93
94     extension_t *p_iter;
95     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
96     {
97         if( !p_iter )
98             break;
99         assert( p_iter->psz_name != NULL );
100         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
101         {
102             vlc_mutex_unlock( &p_mgr->p_sys->lock );
103             return true;
104         }
105     }
106     FOREACH_END()
107
108     vlc_mutex_unlock( &p_mgr->p_sys->lock );
109     return false;
110 }
111
112 /** Recursively drop and free commands starting from "command" */
113 static void FreeCommands( struct command_t *command )
114 {
115     if( !command ) return;
116     struct command_t *next = command->next;
117     switch( command->i_command )
118     {
119         case CMD_ACTIVATE:
120         case CMD_DEACTIVATE:
121         case CMD_CLICK: // Arg1 must not be freed
122             break;
123
124         case CMD_TRIGGERMENU:
125             free( command->data[0] ); // Arg1 is int*, to free
126             break;
127
128         default:
129             break;
130     }
131     free( command );
132     FreeCommands( next );
133 }
134
135 /** Deactivate this extension: pushes immediate command and drops queued */
136 int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
137 {
138     (void) p_mgr;
139     vlc_mutex_lock( &p_ext->p_sys->command_lock );
140
141     if( p_ext->p_sys->b_exiting )
142     {
143         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
144         return VLC_EGENERIC;
145     }
146
147     /* Free the list of commands */
148     FreeCommands( p_ext->p_sys->command );
149
150     /* Push command */
151     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
152     cmd->i_command = CMD_DEACTIVATE;
153     p_ext->p_sys->command = cmd;
154
155     vlc_cond_signal( &p_ext->p_sys->wait );
156     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
157
158     return VLC_SUCCESS;
159 }
160
161 /** Remove an extension from the activated list */
162 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
163 {
164     if( p_mgr->p_sys->b_killed )
165         return VLC_SUCCESS;
166     vlc_mutex_lock( &p_mgr->p_sys->lock );
167
168     int i_idx = -1;
169     extension_t *p_iter;
170     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
171     {
172         i_idx++;
173         if( !p_iter )
174         {
175             i_idx = -1;
176             break;
177         }
178         assert( p_iter->psz_name != NULL );
179         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
180             break;
181     }
182     FOREACH_END()
183
184     if( i_idx >= 0 )
185     {
186         ARRAY_REMOVE( p_mgr->p_sys->activated_extensions, i_idx );
187     }
188     else
189     {
190         msg_Dbg( p_mgr, "Can't find extension '%s' in the activated list",
191                  p_ext->psz_title );
192     }
193
194     vlc_mutex_unlock( &p_mgr->p_sys->lock );
195     return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC;
196 }
197
198 /** Wait for an extension to finish */
199 void WaitForDeactivation( extension_t *p_ext )
200 {
201     vlc_join( p_ext->p_sys->thread, NULL );
202 }
203
204 /** Push a UI command */
205 int __PushCommand( extension_t *p_ext,  bool b_unique, int i_command,
206                    va_list args )
207 {
208     vlc_mutex_lock( &p_ext->p_sys->command_lock );
209
210     /* Create command */
211     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
212     cmd->i_command = i_command;
213     switch( i_command )
214     {
215         case CMD_CLICK:
216             cmd->data[0] = va_arg( args, void* );
217             break;
218         case CMD_TRIGGERMENU:
219             {
220                 int *pi = malloc( sizeof( int ) );
221                 if( !pi )
222                 {
223                     free( cmd );
224                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
225                     return VLC_ENOMEM;
226                 }
227                 *pi = va_arg( args, int );
228                 cmd->data[0] = pi;
229             }
230             break;
231         case CMD_CLOSE:
232         case CMD_SET_INPUT:
233         case CMD_UPDATE_META:
234             // Nothing to do here
235             break;
236         default:
237             msg_Dbg( p_ext->p_sys->p_mgr,
238                      "Unknown command send to extension: %d", i_command );
239             break;
240     }
241
242     /* Push command to the end of the queue */
243     struct command_t *last = p_ext->p_sys->command;
244     if( !last )
245     {
246         p_ext->p_sys->command = cmd;
247     }
248     else
249     {
250         bool b_skip = false;
251         while( last->next != NULL )
252         {
253             if( b_unique && last->i_command == i_command )
254             {
255                 // Do not push this 'unique' command a second time
256                 b_skip = true;
257                 break;
258             }
259             else
260             {
261                 last = last->next;
262             }
263         }
264         if( !b_skip )
265             last->next = cmd;
266     }
267
268     vlc_cond_signal( &p_ext->p_sys->wait );
269     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
270     return VLC_SUCCESS;
271 }
272
273 /* Thread loop */
274 static void* Run( void *data )
275 {
276     extension_t *p_ext = data;
277     extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
278
279     vlc_mutex_lock( &p_ext->p_sys->command_lock );
280
281     while( !p_ext->p_sys->b_exiting )
282     {
283         /* Pop command in front */
284         struct command_t *cmd = p_ext->p_sys->command;
285         if( cmd )
286         {
287             p_ext->p_sys->command = cmd->next;
288             cmd->next = NULL; // This prevents FreeCommands from freeing next
289         }
290
291         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
292
293         /* Run command */
294         if( cmd )
295         {
296             if( LockExtension( p_ext ) )
297             {
298                 switch( cmd->i_command )
299                 {
300                     case CMD_ACTIVATE:
301                     {
302                         if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
303                         {
304                             msg_Dbg( p_mgr, "Could not activate extension!" );
305                             Deactivate( p_mgr, p_ext );
306                         }
307                         break;
308                     }
309
310                     case CMD_DEACTIVATE:
311                     {
312                         msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
313                         if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
314                         {
315                             msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
316                                       p_ext->psz_title );
317                         }
318                         p_ext->p_sys->b_exiting = true;
319                         RemoveActivated( p_mgr, p_ext );
320                         break;
321                     }
322
323                     case CMD_CLOSE:
324                     {
325                         lua_ExecuteFunction( p_mgr, p_ext, "close", LUA_END );
326                         break;
327                     }
328
329                     case CMD_CLICK:
330                     {
331                         extension_widget_t *p_widget = cmd->data[0];
332                         assert( p_widget );
333                         msg_Dbg( p_mgr, "Clicking '%s': '%s'",
334                                  p_ext->psz_name, p_widget->psz_text );
335                         if( !lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget )
336                             < 0 )
337                         {
338                             msg_Warn( p_mgr, "Could not translate click" );
339                         }
340                         break;
341                     }
342
343                     case CMD_TRIGGERMENU:
344                     {
345                         int *pi_id = cmd->data[0];
346                         assert( pi_id );
347                         msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
348                                  *pi_id, p_ext->psz_name );
349                         lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
350                         break;
351                     }
352
353                     case CMD_SET_INPUT:
354                     {
355                         lua_ExecuteFunction( p_mgr, p_ext, "input_changed", LUA_END );
356                         break;
357                     }
358
359                     case CMD_UPDATE_META:
360                     {
361                         lua_ExecuteFunction( p_mgr, p_ext, "meta_changed", LUA_END );
362                         break;
363                     }
364
365                     default:
366                     {
367                         msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
368                                  cmd->i_command );
369                         break;
370                     }
371                 }
372                 UnlockExtension( p_ext );
373             }
374         }
375
376         FreeCommands( cmd );
377
378         vlc_mutex_lock( &p_ext->p_sys->command_lock );
379         if( !p_ext->p_sys->b_exiting && !p_ext->p_sys->command )
380         {
381             vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
382         }
383     }
384
385     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
386     msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
387
388     // Note: At this point, the extension should be deactivated
389     return NULL;
390 }