extension_thread.c 12.1 KB
Newer Older
JPeg's avatar
JPeg committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*****************************************************************************
 * extension_thread.c: Extensions Manager, Threads manager (no Lua here)
 *****************************************************************************
 * Copyright (C) 2009-2010 VideoLAN and authors
 * $Id$
 *
 * Authors: Jean-Philippe André < jpeg # videolan.org >
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

Rafaël Carré's avatar
Rafaël Carré committed
24
25
26
27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include "vlc.h"
JPeg's avatar
JPeg committed
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "extension.h"
#include "assert.h"

struct thread_sys_t
{
    extensions_manager_t *p_mgr;
    extension_t *p_ext;
};

/** Thread Run */
static void* Run( void *data );
static void FreeCommands( struct command_t *command );

/**
 * Activate an extension
 * @param p_mgr This manager
 * @param p_ext Extension to activate
 * @return The usual VLC return codes
 **/
int Activate( extensions_manager_t *p_mgr, extension_t *p_ext )
{
    assert( p_ext != NULL );

    struct extension_sys_t *p_sys = p_ext->p_sys;
    assert( p_sys != NULL );

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    vlc_mutex_lock( &p_sys->command_lock );
    if ( p_sys->b_activated == false )
    {
        /* Prepare first command */
        assert(p_sys->command == NULL);
        p_sys->command = calloc( 1, sizeof( struct command_t ) );
        if( !p_sys->command )
        {
            vlc_mutex_unlock( &p_sys->command_lock );
            return VLC_ENOMEM;
        }
        p_sys->command->i_command = CMD_ACTIVATE; /* No params */
        if (p_sys->b_thread_running == true)
        {
            msg_Dbg( p_mgr, "Reactivating extension %s", p_ext->psz_title);
            vlc_cond_signal( &p_sys->wait );
        }
    }
    vlc_mutex_unlock( &p_sys->command_lock );
JPeg's avatar
JPeg committed
74

75
76
    if (p_sys->b_thread_running == true)
        return VLC_SUCCESS;
JPeg's avatar
JPeg committed
77

78
    msg_Dbg( p_mgr, "Activating extension '%s'", p_ext->psz_title );
JPeg's avatar
JPeg committed
79
80
    /* Start thread */
    p_sys->b_exiting = false;
81
    p_sys->b_thread_running = true;
JPeg's avatar
JPeg committed
82
83
84
85
86

    if( vlc_clone( &p_sys->thread, Run, p_ext, VLC_THREAD_PRIORITY_LOW )
        != VLC_SUCCESS )
    {
        p_sys->b_exiting = true;
87
        p_sys->b_thread_running = false;
JPeg's avatar
JPeg committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
        return VLC_ENOMEM;
    }

    return VLC_SUCCESS;
}

/** Recursively drop and free commands starting from "command" */
static void FreeCommands( struct command_t *command )
{
    if( !command ) return;
    struct command_t *next = command->next;
    switch( command->i_command )
    {
        case CMD_ACTIVATE:
        case CMD_DEACTIVATE:
        case CMD_CLICK: // Arg1 must not be freed
            break;

        case CMD_TRIGGERMENU:
107
        case CMD_PLAYING_CHANGED:
JPeg's avatar
JPeg committed
108
109
            free( command->data[0] ); // Arg1 is int*, to free
            break;
110
111
112

        default:
            break;
JPeg's avatar
JPeg committed
113
114
115
116
117
    }
    free( command );
    FreeCommands( next );
}

118
bool QueueDeactivateCommand( extension_t *p_ext )
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{
    struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
    if( unlikely( cmd == NULL ) )
        return false;
    /* Free the list of commands */
    if( p_ext->p_sys->command )
        FreeCommands( p_ext->p_sys->command->next );

    /* Push command */

    cmd->i_command = CMD_DEACTIVATE;
    if( p_ext->p_sys->command )
        p_ext->p_sys->command->next = cmd;
    else
        p_ext->p_sys->command = cmd;

    vlc_cond_signal( &p_ext->p_sys->wait );
    return true;
}

JPeg's avatar
JPeg committed
139
140
141
142
143
144
145
146
147
148
149
/** Deactivate this extension: pushes immediate command and drops queued */
int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
{
    vlc_mutex_lock( &p_ext->p_sys->command_lock );

    if( p_ext->p_sys->b_exiting )
    {
        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
        return VLC_EGENERIC;
    }

Thomas Guillem's avatar
Thomas Guillem committed
150
    if( p_ext->p_sys->p_progress_id != NULL )
JPeg's avatar
JPeg committed
151
152
    {
        // Extension is stuck, kill it now
Thomas Guillem's avatar
Thomas Guillem committed
153
154
        vlc_dialog_release( p_mgr, p_ext->p_sys->p_progress_id );
        p_ext->p_sys->p_progress_id = NULL;
155
        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
156
        KillExtension( p_mgr, p_ext );
JPeg's avatar
JPeg committed
157
158
159
        return VLC_SUCCESS;
    }

160
    bool b_success = QueueDeactivateCommand( p_ext );
JPeg's avatar
JPeg committed
161
162
    vlc_mutex_unlock( &p_ext->p_sys->command_lock );

163
    return b_success ? VLC_SUCCESS : VLC_ENOMEM;
JPeg's avatar
JPeg committed
164
165
}

JPeg's avatar
JPeg committed
166
167
168
void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext )
{
    msg_Dbg( p_mgr, "Killing extension now" );
169
    vlclua_fd_interrupt( &p_ext->p_sys->dtable );
170

171
    vlc_mutex_lock( &p_ext->p_sys->command_lock );
172
    p_ext->p_sys->b_activated = false;
JPeg's avatar
JPeg committed
173
    p_ext->p_sys->b_exiting = true;
174
    vlc_cond_signal( &p_ext->p_sys->wait );
175
    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
JPeg's avatar
JPeg committed
176
177
}

JPeg's avatar
JPeg committed
178
/** Push a UI command */
179
int PushCommand__( extension_t *p_ext,  bool b_unique, command_type_e i_command,
180
                   va_list args )
JPeg's avatar
JPeg committed
181
182
183
{
    /* Create command */
    struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
184
185
    if( unlikely( cmd == NULL ) )
        return VLC_ENOMEM;
JPeg's avatar
JPeg committed
186
187
188
189
190
191
192
193
194
    cmd->i_command = i_command;
    switch( i_command )
    {
        case CMD_CLICK:
            cmd->data[0] = va_arg( args, void* );
            break;
        case CMD_TRIGGERMENU:
            {
                int *pi = malloc( sizeof( int ) );
195
196
197
198
199
                if( !pi )
                {
                    free( cmd );
                    return VLC_ENOMEM;
                }
JPeg's avatar
JPeg committed
200
201
202
203
                *pi = va_arg( args, int );
                cmd->data[0] = pi;
            }
            break;
204
205
206
207
208
209
210
211
212
213
214
215
        case CMD_PLAYING_CHANGED:
            {
                int *pi = malloc( sizeof( int ) );
                if( !pi )
                {
                    free( cmd );
                    return VLC_ENOMEM;
                }
                *pi = va_arg( args, int );
                cmd->data[0] = pi;
            }
            break;
216
217
218
219
220
        case CMD_CLOSE:
        case CMD_SET_INPUT:
        case CMD_UPDATE_META:
            // Nothing to do here
            break;
JPeg's avatar
JPeg committed
221
222
223
224
225
226
        default:
            msg_Dbg( p_ext->p_sys->p_mgr,
                     "Unknown command send to extension: %d", i_command );
            break;
    }

227
228
    vlc_mutex_lock( &p_ext->p_sys->command_lock );

JPeg's avatar
JPeg committed
229
230
231
232
233
234
235
236
    /* Push command to the end of the queue */
    struct command_t *last = p_ext->p_sys->command;
    if( !last )
    {
        p_ext->p_sys->command = cmd;
    }
    else
    {
237
        bool b_skip = false;
JPeg's avatar
JPeg committed
238
239
        while( last->next != NULL )
        {
240
241
242
            if( b_unique && last->i_command == i_command )
            {
                // Do not push this 'unique' command a second time
243
                b_skip = !memcmp( last->data, cmd->data, sizeof( cmd->data ) );
244
245
246
247
248
249
                break;
            }
            else
            {
                last = last->next;
            }
JPeg's avatar
JPeg committed
250
        }
251
        if( !b_skip )
252
        {
253
254
255
256
257
            last->next = cmd;
        }
        else
        {
            FreeCommands( cmd );
258
        }
JPeg's avatar
JPeg committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
    }

    vlc_cond_signal( &p_ext->p_sys->wait );
    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
    return VLC_SUCCESS;
}

/* Thread loop */
static void* Run( void *data )
{
    extension_t *p_ext = data;
    extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;

    vlc_mutex_lock( &p_ext->p_sys->command_lock );

    while( !p_ext->p_sys->b_exiting )
    {
        struct command_t *cmd = p_ext->p_sys->command;
277
278
279
280
281
282
283
284
285

        /* Pop command in front */
        if( cmd == NULL )
        {
            vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
            continue;
        }
        p_ext->p_sys->command = cmd->next;
        cmd->next = NULL; /* unlink command (for FreeCommands()) */
286
287
        // Create watch timer
        vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD, 0 );
JPeg's avatar
JPeg committed
288
289
290
        vlc_mutex_unlock( &p_ext->p_sys->command_lock );

        /* Run command */
291
        if( LockExtension( p_ext ) )
JPeg's avatar
JPeg committed
292
        {
293
            switch( cmd->i_command )
JPeg's avatar
JPeg committed
294
            {
295
                case CMD_ACTIVATE:
JPeg's avatar
JPeg committed
296
                {
297
                    if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
JPeg's avatar
JPeg committed
298
                    {
299
                        msg_Err( p_mgr, "Could not activate extension!" );
300
301
302
                        vlc_mutex_lock( &p_ext->p_sys->command_lock );
                        QueueDeactivateCommand( p_ext );
                        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
303
                        break;
JPeg's avatar
JPeg committed
304
                    }
305
306
307
                    vlc_mutex_lock( &p_ext->p_sys->command_lock );
                    p_ext->p_sys->b_activated = true;
                    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
308
309
                    break;
                }
JPeg's avatar
JPeg committed
310

311
312
313
314
                case CMD_DEACTIVATE:
                {
                    msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
                    if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
JPeg's avatar
JPeg committed
315
                    {
316
317
                        msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
                                  p_ext->psz_title );
JPeg's avatar
JPeg committed
318
                    }
319
320
321
                    vlc_mutex_lock( &p_ext->p_sys->command_lock );
                    p_ext->p_sys->b_activated = false;
                    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
322
323
                    break;
                }
JPeg's avatar
JPeg committed
324

325
326
327
328
329
                case CMD_CLOSE:
                {
                    lua_ExecuteFunction( p_mgr, p_ext, "close", LUA_END );
                    break;
                }
JPeg's avatar
JPeg committed
330

331
332
333
334
335
336
                case CMD_CLICK:
                {
                    extension_widget_t *p_widget = cmd->data[0];
                    assert( p_widget );
                    msg_Dbg( p_mgr, "Clicking '%s': '%s'",
                             p_ext->psz_name, p_widget->psz_text );
ivoire's avatar
ivoire committed
337
                    if( lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget ) < 0 )
JPeg's avatar
JPeg committed
338
                    {
339
                        msg_Warn( p_mgr, "Could not translate click" );
JPeg's avatar
JPeg committed
340
                    }
341
342
                    break;
                }
JPeg's avatar
JPeg committed
343

344
345
346
347
348
349
350
351
352
                case CMD_TRIGGERMENU:
                {
                    int *pi_id = cmd->data[0];
                    assert( pi_id );
                    msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
                             *pi_id, p_ext->psz_name );
                    lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
                    break;
                }
JPeg's avatar
JPeg committed
353

354
355
356
357
358
                case CMD_SET_INPUT:
                {
                    lua_ExecuteFunction( p_mgr, p_ext, "input_changed", LUA_END );
                    break;
                }
359

360
361
362
363
364
                case CMD_UPDATE_META:
                {
                    lua_ExecuteFunction( p_mgr, p_ext, "meta_changed", LUA_END );
                    break;
                }
365

366
367
368
369
370
371
                case CMD_PLAYING_CHANGED:
                {
                    lua_ExecuteFunction( p_mgr, p_ext, "playing_changed",
                            LUA_NUM, *((int *)cmd->data[0]), LUA_END );
                    break;
                }
372

373
374
375
376
377
                default:
                {
                    msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
                             cmd->i_command );
                    break;
JPeg's avatar
JPeg committed
378
379
                }
            }
380
            UnlockExtension( p_ext );
JPeg's avatar
JPeg committed
381
        }
382
        FreeCommands( cmd );
JPeg's avatar
JPeg committed
383

ivoire's avatar
ivoire committed
384
        vlc_mutex_lock( &p_ext->p_sys->command_lock );
385
386
387
388
389
390
391
        // Reset watch timer and timestamp
        if( p_ext->p_sys->p_progress_id != NULL )
        {
            vlc_dialog_release( p_mgr, p_ext->p_sys->p_progress_id );
            p_ext->p_sys->p_progress_id = NULL;
        }
        vlc_timer_schedule( p_ext->p_sys->timer, false, 0, 0 );
JPeg's avatar
JPeg committed
392
393
    }

394
    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
395
    msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
JPeg's avatar
JPeg committed
396
397
398
399

    // Note: At this point, the extension should be deactivated
    return NULL;
}