]> git.sesse.net Git - vlc/blob - modules/lua/extension_thread.c
Improve vlc_memalign()
[vlc] / modules / 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  * @todo FIXME Should be entered with the lock held
90  **/
91 bool IsActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
92 {
93     assert( p_ext != NULL );
94     vlc_mutex_lock( &p_mgr->p_sys->lock );
95
96     extension_t *p_iter;
97     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
98     {
99         if( !p_iter )
100             break;
101         assert( p_iter->psz_name != NULL );
102         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
103         {
104             vlc_mutex_unlock( &p_mgr->p_sys->lock );
105             return true;
106         }
107     }
108     FOREACH_END()
109
110     vlc_mutex_unlock( &p_mgr->p_sys->lock );
111     return false;
112 }
113
114 /** Recursively drop and free commands starting from "command" */
115 static void FreeCommands( struct command_t *command )
116 {
117     if( !command ) return;
118     struct command_t *next = command->next;
119     switch( command->i_command )
120     {
121         case CMD_ACTIVATE:
122         case CMD_DEACTIVATE:
123         case CMD_CLICK: // Arg1 must not be freed
124             break;
125
126         case CMD_TRIGGERMENU:
127         case CMD_PLAYING_CHANGED:
128             free( command->data[0] ); // Arg1 is int*, to free
129             break;
130
131         default:
132             break;
133     }
134     free( command );
135     FreeCommands( next );
136 }
137
138 /** Deactivate this extension: pushes immediate command and drops queued */
139 int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
140 {
141     (void) p_mgr;
142     vlc_mutex_lock( &p_ext->p_sys->command_lock );
143
144     if( p_ext->p_sys->b_exiting )
145     {
146         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
147         return VLC_EGENERIC;
148     }
149
150     if( p_ext->p_sys->progress )
151     {
152         // Extension is stuck, kill it now
153         dialog_ProgressDestroy( p_ext->p_sys->progress );
154         p_ext->p_sys->progress = NULL;
155         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
156         KillExtension( p_mgr, p_ext );
157         return VLC_SUCCESS;
158     }
159
160     /* Free the list of commands */
161     if( p_ext->p_sys->command )
162         FreeCommands( p_ext->p_sys->command->next );
163
164     /* Push command */
165     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
166     cmd->i_command = CMD_DEACTIVATE;
167     if( p_ext->p_sys->command )
168         p_ext->p_sys->command->next = cmd;
169     else
170         p_ext->p_sys->command = cmd;
171
172     vlc_cond_signal( &p_ext->p_sys->wait );
173     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
174
175     return VLC_SUCCESS;
176 }
177
178 /** Remove an extension from the activated list */
179 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
180 {
181     if( p_mgr->p_sys->b_killed )
182         return VLC_SUCCESS;
183     vlc_mutex_lock( &p_mgr->p_sys->lock );
184
185     int i_idx = -1;
186     extension_t *p_iter;
187     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
188     {
189         i_idx++;
190         if( !p_iter )
191         {
192             i_idx = -1;
193             break;
194         }
195         assert( p_iter->psz_name != NULL );
196         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
197             break;
198     }
199     FOREACH_END()
200
201     if( i_idx >= 0 )
202     {
203         ARRAY_REMOVE( p_mgr->p_sys->activated_extensions, i_idx );
204     }
205     else
206     {
207         msg_Dbg( p_mgr, "Can't find extension '%s' in the activated list",
208                  p_ext->psz_title );
209     }
210
211     vlc_mutex_unlock( &p_mgr->p_sys->lock );
212     return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC;
213 }
214
215 void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext )
216 {
217     /* Cancel thread if it seems stuck for a while */
218     msg_Dbg( p_mgr, "Killing extension now" );
219     vlc_cancel( p_ext->p_sys->thread );
220     lua_ExtensionDeactivate( p_mgr, p_ext );
221     p_ext->p_sys->b_exiting = true;
222     RemoveActivated( p_mgr, p_ext );
223 }
224
225 /** Push a UI command */
226 int __PushCommand( extension_t *p_ext,  bool b_unique, command_type_e i_command,
227                    va_list args )
228 {
229     vlc_mutex_lock( &p_ext->p_sys->command_lock );
230
231     /* Create command */
232     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
233     cmd->i_command = i_command;
234     switch( i_command )
235     {
236         case CMD_CLICK:
237             cmd->data[0] = va_arg( args, void* );
238             break;
239         case CMD_TRIGGERMENU:
240             {
241                 int *pi = malloc( sizeof( int ) );
242                 if( !pi )
243                 {
244                     free( cmd );
245                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
246                     return VLC_ENOMEM;
247                 }
248                 *pi = va_arg( args, int );
249                 cmd->data[0] = pi;
250             }
251             break;
252         case CMD_PLAYING_CHANGED:
253             {
254                 int *pi = malloc( sizeof( int ) );
255                 if( !pi )
256                 {
257                     free( cmd );
258                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
259                     return VLC_ENOMEM;
260                 }
261                 *pi = va_arg( args, int );
262                 cmd->data[0] = pi;
263             }
264             break;
265         case CMD_CLOSE:
266         case CMD_SET_INPUT:
267         case CMD_UPDATE_META:
268             // Nothing to do here
269             break;
270         default:
271             msg_Dbg( p_ext->p_sys->p_mgr,
272                      "Unknown command send to extension: %d", i_command );
273             break;
274     }
275
276     /* Push command to the end of the queue */
277     struct command_t *last = p_ext->p_sys->command;
278     if( !last )
279     {
280         p_ext->p_sys->command = cmd;
281     }
282     else
283     {
284         bool b_skip = false;
285         while( last->next != NULL )
286         {
287             if( b_unique && last->i_command == i_command )
288             {
289                 // Do not push this 'unique' command a second time
290                 b_skip = !memcmp( last->data, cmd->data, sizeof( cmd->data ) );
291                 break;
292             }
293             else
294             {
295                 last = last->next;
296             }
297         }
298         if( !b_skip )
299         {
300             last->next = cmd;
301         }
302         else
303         {
304             FreeCommands( cmd );
305         }
306     }
307
308     vlc_cond_signal( &p_ext->p_sys->wait );
309     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
310     return VLC_SUCCESS;
311 }
312
313 /* Thread loop */
314 static void* Run( void *data )
315 {
316     extension_t *p_ext = data;
317     extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
318
319     int cancel = vlc_savecancel();
320     vlc_mutex_lock( &p_ext->p_sys->command_lock );
321
322     while( !p_ext->p_sys->b_exiting )
323     {
324         /* Pop command in front */
325         struct command_t *cmd = p_ext->p_sys->command;
326         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
327         vlc_restorecancel( cancel );
328
329         /* Run command */
330         if( cmd )
331         {
332             if( LockExtension( p_ext ) )
333             {
334                 mutex_cleanup_push( &p_ext->p_sys->running_lock );
335                 switch( cmd->i_command )
336                 {
337                     case CMD_ACTIVATE:
338                     {
339                         if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
340                         {
341                             msg_Err( p_mgr, "Could not activate extension!" );
342                             Deactivate( p_mgr, p_ext );
343                             cmd = NULL;
344                         }
345                         break;
346                     }
347
348                     case CMD_DEACTIVATE:
349                     {
350                         msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
351                         if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
352                         {
353                             msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
354                                       p_ext->psz_title );
355                         }
356                         p_ext->p_sys->b_exiting = true;
357                         RemoveActivated( p_mgr, p_ext );
358                         break;
359                     }
360
361                     case CMD_CLOSE:
362                     {
363                         lua_ExecuteFunction( p_mgr, p_ext, "close", LUA_END );
364                         break;
365                     }
366
367                     case CMD_CLICK:
368                     {
369                         extension_widget_t *p_widget = cmd->data[0];
370                         assert( p_widget );
371                         msg_Dbg( p_mgr, "Clicking '%s': '%s'",
372                                  p_ext->psz_name, p_widget->psz_text );
373                         if( !lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget )
374                             < 0 )
375                         {
376                             msg_Warn( p_mgr, "Could not translate click" );
377                         }
378                         break;
379                     }
380
381                     case CMD_TRIGGERMENU:
382                     {
383                         int *pi_id = cmd->data[0];
384                         assert( pi_id );
385                         msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
386                                  *pi_id, p_ext->psz_name );
387                         lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
388                         break;
389                     }
390
391                     case CMD_SET_INPUT:
392                     {
393                         lua_ExecuteFunction( p_mgr, p_ext, "input_changed", LUA_END );
394                         break;
395                     }
396
397                     case CMD_UPDATE_META:
398                     {
399                         lua_ExecuteFunction( p_mgr, p_ext, "meta_changed", LUA_END );
400                         break;
401                     }
402
403                     case CMD_PLAYING_CHANGED:
404                     {
405                         lua_ExecuteFunction( p_mgr, p_ext, "playing_changed",
406                                 LUA_NUM, *((int *)cmd->data[0]), LUA_END );
407                         break;
408                     }
409
410                     default:
411                     {
412                         msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
413                                  cmd->i_command );
414                         break;
415                     }
416                 }
417                 vlc_cleanup_pop();
418                 UnlockExtension( p_ext );
419             }
420         }
421
422         cancel = vlc_savecancel();
423         vlc_mutex_lock( &p_ext->p_sys->command_lock );
424         if( p_ext->p_sys->command )
425         {
426             cmd = p_ext->p_sys->command;
427             p_ext->p_sys->command = cmd->next;
428             cmd->next = NULL; // This prevents FreeCommands from freeing next
429             FreeCommands( cmd );
430         }
431
432         if( !p_ext->p_sys->b_exiting && !p_ext->p_sys->command )
433         {
434             vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
435         }
436     }
437
438     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
439     msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
440     vlc_restorecancel( cancel );
441
442     // Note: At this point, the extension should be deactivated
443     return NULL;
444 }