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