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