extension_thread.c 11.8 KB
Newer Older
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"
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 );
74

75 76
    if (p_sys->b_thread_running == true)
        return VLC_SUCCESS;
77

78
    msg_Dbg( p_mgr, "Activating extension '%s'", p_ext->psz_title );
79 80
    /* Start thread */
    p_sys->b_exiting = false;
81
    p_sys->b_thread_running = true;
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;
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:
108 109
            free( command->data[0] ); // Arg1 is int*, to free
            break;
110 111 112

        default:
            break;
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;
}

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 )
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
        KillExtension( p_mgr, p_ext );
156
        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
157 158 159
        return VLC_SUCCESS;
    }

160
    bool b_success = QueueDeactivateCommand( p_ext );
161 162
    vlc_mutex_unlock( &p_ext->p_sys->command_lock );

163
    return b_success ? VLC_SUCCESS : VLC_ENOMEM;
164 165
}

166
/* MUST be called with command_lock held */
167 168 169
void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext )
{
    msg_Dbg( p_mgr, "Killing extension now" );
170
    vlclua_fd_interrupt( &p_ext->p_sys->dtable );
171
    p_ext->p_sys->b_activated = false;
172
    p_ext->p_sys->b_exiting = true;
173
    vlc_cond_signal( &p_ext->p_sys->wait );
174 175
}

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

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

227 228 229 230 231 232 233 234
    /* 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
    {
235
        bool b_skip = false;
236 237
        while( last->next != NULL )
        {
238 239 240
            if( b_unique && last->i_command == i_command )
            {
                // Do not push this 'unique' command a second time
241
                b_skip = !memcmp( last->data, cmd->data, sizeof( cmd->data ) );
242 243 244 245 246 247
                break;
            }
            else
            {
                last = last->next;
            }
248
        }
249
        if( !b_skip )
250
        {
251 252 253 254 255
            last->next = cmd;
        }
        else
        {
            FreeCommands( cmd );
256
        }
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    }

    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;
275 276 277 278 279 280 281 282 283

        /* 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()) */
284 285
        // Create watch timer
        vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD, 0 );
286 287 288
        vlc_mutex_unlock( &p_ext->p_sys->command_lock );

        /* Run command */
289 290
        vlc_mutex_lock( &p_ext->p_sys->running_lock );
        switch( cmd->i_command )
291
        {
292
            case CMD_ACTIVATE:
293
            {
294
                if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
295
                {
296
                    msg_Err( p_mgr, "Could not activate extension!" );
297
                    vlc_mutex_lock( &p_ext->p_sys->command_lock );
298
                    QueueDeactivateCommand( p_ext );
299
                    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
300 301
                    break;
                }
302 303 304 305 306
                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 );
                break;
            }
307

308 309 310 311
            case CMD_DEACTIVATE:
            {
                msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
                if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
312
                {
313 314
                    msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
                              p_ext->psz_title );
315
                }
316 317 318 319 320
                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 );
                break;
            }
321

322 323 324 325 326
            case CMD_CLOSE:
            {
                lua_ExecuteFunction( p_mgr, p_ext, "close", LUA_END );
                break;
            }
327

328 329 330 331 332 333 334
            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 );
                if( lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget ) < 0 )
335
                {
336
                    msg_Warn( p_mgr, "Could not translate click" );
337
                }
338 339
                break;
            }
340

341 342 343 344 345 346 347 348 349
            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;
            }
350

351 352 353 354 355
            case CMD_SET_INPUT:
            {
                lua_ExecuteFunction( p_mgr, p_ext, "input_changed", LUA_END );
                break;
            }
356

357 358 359 360 361
            case CMD_UPDATE_META:
            {
                lua_ExecuteFunction( p_mgr, p_ext, "meta_changed", LUA_END );
                break;
            }
362

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

370 371 372 373 374
            default:
            {
                msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
                         cmd->i_command );
                break;
375 376
            }
        }
377 378
        vlc_mutex_unlock( &p_ext->p_sys->running_lock );

379
        FreeCommands( cmd );
380

381
        vlc_mutex_lock( &p_ext->p_sys->command_lock );
382 383 384 385 386 387 388
        // 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 );
389 390
    }

391
    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
392
    msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
393 394 395 396

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