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