gnome.c 16 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * gnome.c : Gnome plugin for vlc
 *****************************************************************************
 * Copyright (C) 2000 VideoLAN
Gildas Bazin's avatar
 
Gildas Bazin committed
5
 * $Id: gnome.c,v 1.12 2002/03/11 20:14:16 gbazin Exp $
6
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
7 8
 * Authors: Samuel Hocevar <sam@zoy.org>
 *      
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
Sam Hocevar's avatar
 
Sam Hocevar committed
28 29 30 31 32 33 34 35 36 37
#include <errno.h>                                                 /* ENOMEM */
#include <string.h>                                            /* strerror() */
#include <stdio.h>

#include <videolan/vlc.h>

#include <gnome.h>

#include "stream_control.h"
#include "input_ext-intf.h"
38

Sam Hocevar's avatar
 
Sam Hocevar committed
39 40
#include "interface.h"
#include "intf_playlist.h"
41

Sam Hocevar's avatar
 
Sam Hocevar committed
42 43 44 45 46 47 48 49
#include "video.h"
#include "video_output.h"

#include "gnome_callbacks.h"
#include "gnome_interface.h"
#include "gnome_support.h"
#include "gtk_display.h"
#include "gtk_common.h"
50 51

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
52
 * Local prototypes.
Sam Hocevar's avatar
 
Sam Hocevar committed
53
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
54 55 56 57 58 59
static void intf_getfunctions( function_list_t * p_function_list );
static int  intf_Open        ( intf_thread_t *p_intf );
static void intf_Close       ( intf_thread_t *p_intf );
static void intf_Run         ( intf_thread_t *p_intf );

static gint GnomeManage      ( gpointer p_data );
Sam Hocevar's avatar
 
Sam Hocevar committed
60 61

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
62
 * Building configuration tree
63
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
64 65
MODULE_CONFIG_START
MODULE_CONFIG_STOP
66

Sam Hocevar's avatar
 
Sam Hocevar committed
67
MODULE_INIT_START
Sam Hocevar's avatar
 
Sam Hocevar committed
68 69 70 71 72 73 74 75 76 77 78
    SET_DESCRIPTION( "Gnome interface module" )
#ifndef WIN32
    if( getenv( "DISPLAY" ) == NULL )
    {
        ADD_CAPABILITY( INTF, 15 )
    }
    else
#endif
    {
        ADD_CAPABILITY( INTF, 100 )
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
79
    ADD_SHORTCUT( "gnome" )
Sam Hocevar's avatar
 
Sam Hocevar committed
80
    ADD_PROGRAM( "gnome-vlc" )
Sam Hocevar's avatar
 
Sam Hocevar committed
81
MODULE_INIT_STOP
82

Sam Hocevar's avatar
 
Sam Hocevar committed
83
MODULE_ACTIVATE_START
Sam Hocevar's avatar
 
Sam Hocevar committed
84
    intf_getfunctions( &p_module->p_functions->intf );
Sam Hocevar's avatar
 
Sam Hocevar committed
85
MODULE_ACTIVATE_STOP
Sam Hocevar's avatar
 
Sam Hocevar committed
86

Sam Hocevar's avatar
 
Sam Hocevar committed
87 88
MODULE_DEACTIVATE_START
MODULE_DEACTIVATE_STOP
89

Sam Hocevar's avatar
 
Sam Hocevar committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
/*****************************************************************************
 * g_atexit: kludge to avoid the Gnome thread to segfault at exit
 *****************************************************************************
 * gtk_init() makes several calls to g_atexit() which calls atexit() to
 * register tidying callbacks to be called at program exit. Since the Gnome
 * plugin is likely to be unloaded at program exit, we have to export this
 * symbol to intercept the g_atexit() calls. Talk about crude hack.
 *****************************************************************************/
void g_atexit( GVoidFunc func )
{
    intf_thread_t *p_intf = p_main->p_intf;
    int i_dummy;

    for( i_dummy = 0;
         i_dummy < MAX_ATEXIT && p_intf->p_sys->pf_callback[i_dummy] != NULL;
         i_dummy++ )
    {
        ;
    }

    if( i_dummy >= MAX_ATEXIT - 1 )
    {
        intf_ErrMsg( "intf error: too many atexit() callbacks to register" );
        return;
    }

    p_intf->p_sys->pf_callback[i_dummy]     = func;
    p_intf->p_sys->pf_callback[i_dummy + 1] = NULL;
}

/*****************************************************************************
 * Functions exported as capabilities. They are declared as static so that
 * we don't pollute the namespace too much.
 *****************************************************************************/
static void intf_getfunctions( function_list_t * p_function_list )
{
    p_function_list->functions.intf.pf_open  = intf_Open;
    p_function_list->functions.intf.pf_close = intf_Close;
    p_function_list->functions.intf.pf_run   = intf_Run;
}

/*****************************************************************************
 * intf_Open: initialize and create window
 *****************************************************************************/
static int intf_Open( intf_thread_t *p_intf )
{
    /* Allocate instance and initialize some members */
    p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
    if( p_intf->p_sys == NULL )
    {
        intf_ErrMsg("error: %s", strerror(ENOMEM));
        return( 1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
144 145
    p_intf->p_sys->p_sub = intf_MsgSub();

Sam Hocevar's avatar
 
Sam Hocevar committed
146
    /* Initialize Gnome thread */
Stéphane Borel's avatar
 
Stéphane Borel committed
147
    p_intf->p_sys->b_playing = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    p_intf->p_sys->b_popup_changed = 0;
    p_intf->p_sys->b_window_changed = 0;
    p_intf->p_sys->b_playlist_changed = 0;

    p_intf->p_sys->i_playing = -1;
    p_intf->p_sys->b_slider_free = 1;

    p_intf->p_sys->pf_callback[0] = NULL;

    return( 0 );
}

/*****************************************************************************
 * intf_Close: destroy interface window
 *****************************************************************************/
static void intf_Close( intf_thread_t *p_intf )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
165 166
    intf_MsgUnsub( p_intf->p_sys->p_sub );

Sam Hocevar's avatar
 
Sam Hocevar committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    /* Destroy structure */
    free( p_intf->p_sys );
}

/*****************************************************************************
 * intf_Run: Gnome thread
 *****************************************************************************
 * this part of the interface is in a separate thread so that we can call
 * gtk_main() from within it without annoying the rest of the program.
 * XXX: the approach may look kludgy, and probably is, but I could not find
 * a better way to dynamically load a Gnome interface at runtime.
 *****************************************************************************/
static void intf_Run( intf_thread_t *p_intf )
{
    /* gnome_init needs to know the command line. We don't care, so we
     * give it an empty one */
    char *p_args[] = { "" };
    int   i_args   = 1;
    int   i_dummy;

    /* The data types we are allowed to receive */
    static GtkTargetEntry target_table[] =
    {
        { "STRING", 0, DROP_ACCEPT_STRING },
        { "text/uri-list", 0, DROP_ACCEPT_TEXT_URI_LIST },
        { "text/plain",    0, DROP_ACCEPT_TEXT_PLAIN }
    };

    /* Initialize Gnome */
Sam Hocevar's avatar
 
Sam Hocevar committed
196
    gnome_init( p_main->psz_arg0, VERSION, i_args, p_args );
Sam Hocevar's avatar
 
Sam Hocevar committed
197 198 199 200 201

    /* Create some useful widgets that will certainly be used */
    p_intf->p_sys->p_window = create_intf_window( );
    p_intf->p_sys->p_popup = create_intf_popup( );
    p_intf->p_sys->p_playlist = create_intf_playlist();
Sam Hocevar's avatar
 
Sam Hocevar committed
202
    p_intf->p_sys->p_messages = create_intf_messages();
Sam Hocevar's avatar
 
Sam Hocevar committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

    /* Set the title of the main window */
    gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
                          VOUT_TITLE " (Gnome interface)");

    /* Accept file drops on the main window */
    gtk_drag_dest_set( GTK_WIDGET( p_intf->p_sys->p_window ),
                       GTK_DEST_DEFAULT_ALL, target_table,
                       1, GDK_ACTION_COPY );
    /* Accept file drops on the playlist window */
    gtk_drag_dest_set( GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
                            p_intf->p_sys->p_playlist ), "playlist_clist") ),
                       GTK_DEST_DEFAULT_ALL, target_table,
                       1, GDK_ACTION_COPY );

Sam Hocevar's avatar
 
Sam Hocevar committed
218
    /* Get the slider object */
Sam Hocevar's avatar
 
Sam Hocevar committed
219 220
    p_intf->p_sys->p_slider_frame = gtk_object_get_data(
                      GTK_OBJECT( p_intf->p_sys->p_window ), "slider_frame" );
Sam Hocevar's avatar
 
Sam Hocevar committed
221 222 223 224 225 226 227 228

    /* Configure the log window */
    p_intf->p_sys->p_messages_text = GTK_TEXT( gtk_object_get_data(
        GTK_OBJECT(p_intf->p_sys->p_messages ), "messages_textbox" ) );
    gtk_text_set_line_wrap( p_intf->p_sys->p_messages_text, TRUE);
    gtk_text_set_word_wrap( p_intf->p_sys->p_messages_text, FALSE);

    /* Get the interface labels */
Sam Hocevar's avatar
 
Sam Hocevar committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    #define P_LABEL( name ) GTK_LABEL( gtk_object_get_data( \
                         GTK_OBJECT( p_intf->p_sys->p_window ), name ) )
    p_intf->p_sys->p_label_title = P_LABEL( "title_label" );
    p_intf->p_sys->p_label_chapter = P_LABEL( "chapter_label" );
    #undef P_LABEL

    /* Connect the date display to the slider */
    #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
                         GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
    p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );

    gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
                         GTK_SIGNAL_FUNC( GtkDisplayDate ), NULL );
    p_intf->p_sys->f_adj_oldvalue = 0;
    #undef P_SLIDER

    /* We don't create these ones yet because we perhaps won't need them */
    p_intf->p_sys->p_about = NULL;
    p_intf->p_sys->p_modules = NULL;
    p_intf->p_sys->p_fileopen = NULL;
    p_intf->p_sys->p_disc = NULL;
    p_intf->p_sys->p_network = NULL;
    p_intf->p_sys->p_jump = NULL;

    /* Store p_intf to keep an eye on it */
    gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
                         "p_intf", p_intf );

    gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_popup),
                         "p_intf", p_intf );

    gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_playlist ),
                         "p_intf", p_intf );

Sam Hocevar's avatar
 
Sam Hocevar committed
263 264 265
    gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_messages ),
                         "p_intf", p_intf );

Sam Hocevar's avatar
 
Sam Hocevar committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
                         "p_intf", p_intf );

    /* Show the control window */
    gtk_widget_show( p_intf->p_sys->p_window );

    /* Sleep to avoid using all CPU - since some interfaces needs to access
     * keyboard events, a 100ms delay is a good compromise */
    i_dummy = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, GnomeManage, p_intf );

    /* Enter gnome mode */
    gtk_main();

    /* Remove the timeout */
    gtk_timeout_remove( i_dummy );

    /* Get rid of stored callbacks so we can unload the plugin */
    for( i_dummy = 0;
         i_dummy < MAX_ATEXIT && p_intf->p_sys->pf_callback[i_dummy] != NULL;
         i_dummy++ )
    {
        p_intf->p_sys->pf_callback[i_dummy]();
    }
}

/* following functions are local */

/*****************************************************************************
 * GnomeManage: manage main thread messages
 *****************************************************************************
 * In this function, called approx. 10 times a second, we check what the
 * main program wanted to tell us.
 *****************************************************************************/
static gint GnomeManage( gpointer p_data )
{
#define p_intf ((intf_thread_t *)p_data)
Sam Hocevar's avatar
 
Sam Hocevar committed
302 303 304 305 306 307
    static GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
    static GdkColor red   = { 0, 0xffff, 0x6666, 0x6666 };
    static GdkColor gray  = { 0, 0xaaaa, 0xaaaa, 0xaaaa };
    GdkColor *p_color;

    int i_start, i_stop;
Sam Hocevar's avatar
 
Sam Hocevar committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

    vlc_mutex_lock( &p_intf->change_lock );

    /* If the "display popup" flag has changed */
    if( p_intf->b_menu_change )
    {
        if( !GTK_IS_WIDGET( p_intf->p_sys->p_popup ) )
        {
            p_intf->p_sys->p_popup = create_intf_popup();
            gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_popup ),
                                 "p_popup", p_intf );
        }

        gnome_popup_menu_do_popup( p_intf->p_sys->p_popup,
                                   NULL, NULL, NULL, NULL );
        p_intf->b_menu_change = 0;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    /* Update the log window */
    vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
    i_stop = *p_intf->p_sys->p_sub->pi_stop;
    vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );

    if( p_intf->p_sys->p_sub->i_start != i_stop )
    {
        for( i_start = p_intf->p_sys->p_sub->i_start;
             i_start != i_stop;
             i_start = (i_start+1) % INTF_MSG_QSIZE )
        {
            /* Append all messages to log window */
            switch( p_intf->p_sys->p_sub->p_msg[i_start].i_type )
            {
            case INTF_MSG_ERR:
                p_color = &red;
                break;
            case INTF_MSG_WARN:
                p_color = &gray;
                break;
            default:
                p_color = &white;
                break;
            }

            gtk_text_insert( p_intf->p_sys->p_messages_text, NULL, p_color,
                NULL, p_intf->p_sys->p_sub->p_msg[i_start].psz_msg, -1 );
            gtk_text_insert( p_intf->p_sys->p_messages_text, NULL, p_color,
                NULL, "\n", -1 );
        }

        vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
        p_intf->p_sys->p_sub->i_start = i_start;
        vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );

        gtk_text_set_point( p_intf->p_sys->p_messages_text,
                    gtk_text_get_length( p_intf->p_sys->p_messages_text ) );
    }

    /* Update the playlist */
Sam Hocevar's avatar
 
Sam Hocevar committed
366 367
    GtkPlayListManage( p_intf ); 

Sam Hocevar's avatar
 
Sam Hocevar committed
368
    if( p_input_bank->pp_input[0] != NULL && !p_intf->b_die )
Sam Hocevar's avatar
 
Sam Hocevar committed
369
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
370
        vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
371

Sam Hocevar's avatar
 
Sam Hocevar committed
372
        if( !p_input_bank->pp_input[0]->b_die )
Sam Hocevar's avatar
 
Sam Hocevar committed
373
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
374 375
            /* New input or stream map change */
            if( p_input_bank->pp_input[0]->stream.b_changed )
Sam Hocevar's avatar
 
Sam Hocevar committed
376
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
377 378 379
                GtkModeManage( p_intf );
                GtkSetupMenus( p_intf );
                p_intf->p_sys->b_playing = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
380
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
381 382

            /* Manage the slider */
Stéphane Borel's avatar
 
Stéphane Borel committed
383 384
            if( p_input_bank->pp_input[0]->stream.b_seekable &&
                p_intf->p_sys->b_playing )
Sam Hocevar's avatar
 
Sam Hocevar committed
385
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
386 387
                float           newvalue;
                newvalue = p_intf->p_sys->p_adj->value;
Sam Hocevar's avatar
 
Sam Hocevar committed
388
    
Sam Hocevar's avatar
 
Sam Hocevar committed
389 390 391 392 393 394 395 396
#define p_area p_input_bank->pp_input[0]->stream.p_selected_area
                /* If the user hasn't touched the slider since the last time,
                 * then the input can safely change it */
                if( newvalue == p_intf->p_sys->f_adj_oldvalue )
                {
                    /* Update the value */
                    p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue =
                        ( 100. * p_area->i_tell ) / p_area->i_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
397
    
Sam Hocevar's avatar
 
Sam Hocevar committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
                    gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
                                             "value_changed" );
                }
                /* Otherwise, send message to the input if the user has
                 * finished dragging the slider */
                else if( p_intf->p_sys->b_slider_free )
                {
                    off_t i_seek = ( newvalue * p_area->i_size ) / 100;
        
                    vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
                    input_Seek( p_input_bank->pp_input[0], i_seek );
                    vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
    
                    /* Update the old value */
                    p_intf->p_sys->f_adj_oldvalue = newvalue;
                }
Sam Hocevar's avatar
 
Sam Hocevar committed
414
#undef p_area
Sam Hocevar's avatar
 
Sam Hocevar committed
415
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
416

Sam Hocevar's avatar
 
Sam Hocevar committed
417 418 419 420 421 422
            if( p_intf->p_sys->i_part !=
                p_input_bank->pp_input[0]->stream.p_selected_area->i_part )
            {
                p_intf->p_sys->b_chapter_update = 1;
                GtkSetupMenus( p_intf );
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
423 424
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
425
        vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
    }
    else if( p_intf->p_sys->b_playing && !p_intf->b_die )
    {
        GtkModeManage( p_intf );
        p_intf->p_sys->b_playing = 0;
    }

    /* Manage core vlc functions through the callback */
    p_intf->pf_manage( p_intf );

    if( p_intf->b_die )
    {
        vlc_mutex_unlock( &p_intf->change_lock );

        /* Prepare to die, young Skywalker */
        gtk_main_quit();

        /* Just in case */
        return( FALSE );
    }

    vlc_mutex_unlock( &p_intf->change_lock );

    return( TRUE );

#undef p_intf
}