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