1 /*****************************************************************************
2 * familiar.c : familiar plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: familiar.c,v 1.34 2003/03/30 18:14:37 gbazin Exp $
7 * Authors: Jean-Paul Saman <jpsaman@wxs.nl>
8 * Marc Ariberti <marcari@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
29 #include <errno.h> /* ENOMEM */
30 #include <string.h> /* strerror() */
38 #ifdef HAVE_GPE_INIT_H
42 #include "callbacks.h"
43 #include "interface.h"
47 /*****************************************************************************
49 *****************************************************************************/
50 static int Open ( vlc_object_t * );
51 static void Close ( vlc_object_t * );
52 static void Run ( intf_thread_t * );
54 void GtkAutoPlayFile ( vlc_object_t * );
55 static int Manage ( intf_thread_t *p_intf );
56 void E_(GtkDisplayDate) ( GtkAdjustment *p_adj );
57 gint E_(GtkModeManage) ( intf_thread_t * p_intf );
59 /*****************************************************************************
61 *****************************************************************************/
62 #define AUTOPLAYFILE_TEXT N_("autoplay selected file")
63 #define AUTOPLAYFILE_LONGTEXT N_("automatically play a file when selected in the "\
64 "file selection list")
66 /*****************************************************************************
68 *****************************************************************************/
70 add_category_hint( N_("Miscellaneous"), NULL, VLC_TRUE );
71 add_bool( "familiar-autoplayfile", 1, GtkAutoPlayFile, AUTOPLAYFILE_TEXT, AUTOPLAYFILE_LONGTEXT, VLC_TRUE );
72 set_description( _("Familiar Linux Gtk+ interface") );
73 set_capability( "interface", 70 );
74 set_callbacks( Open, Close );
77 /*****************************************************************************
78 * Open: initialize and create window
79 *****************************************************************************/
80 static int Open( vlc_object_t *p_this )
82 intf_thread_t *p_intf = (intf_thread_t *)p_this;
84 /* Allocate instance and initialize some members */
85 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
86 if( p_intf->p_sys == NULL )
88 msg_Err( p_intf, "out of memory" );
93 msg_Dbg( p_intf, "Using gui-helper" );
94 p_intf->p_sys->p_gtk_main = module_Need( p_this, "gui-helper", "gtk" );
95 if( p_intf->p_sys->p_gtk_main == NULL )
97 free( p_intf->p_sys );
102 /* Initialize Gtk+ thread */
103 p_intf->p_sys->p_input = NULL;
105 p_intf->p_sys->b_autoplayfile = 1;
106 p_intf->p_sys->b_playing = 0;
107 p_intf->p_sys->b_slider_free = 1;
109 p_intf->pf_run = Run;
114 /*****************************************************************************
115 * Close: destroy interface window
116 *****************************************************************************/
117 static void Close( vlc_object_t *p_this )
119 intf_thread_t *p_intf = (intf_thread_t *)p_this;
121 if( p_intf->p_sys->p_input )
123 vlc_object_release( p_intf->p_sys->p_input );
127 msg_Dbg( p_intf, "Releasing gui-helper" );
128 module_Unneed( p_intf, p_intf->p_sys->p_gtk_main );
131 /* Destroy structure */
132 free( p_intf->p_sys );
135 /*****************************************************************************
137 *****************************************************************************
138 * this part of the interface is in a separate thread so that we can call
139 * gtk_main() from within it without annoying the rest of the program.
140 *****************************************************************************/
141 static void Run( intf_thread_t *p_intf )
143 #ifndef NEED_GTK_MAIN
144 /* gtk_init needs to know the command line. We don't care, so we
145 * give it an empty one */
146 char *p_args[] = { "", NULL };
147 char **pp_args = p_args;
152 #ifdef HAVE_GPE_INIT_H
153 /* Initialize GPE interface */
154 msg_Dbg( p_intf, "Starting familiar GPE interface" );
155 if (gpe_application_init(&i_args, &pp_args) == FALSE)
159 # ifndef NEED_GTK_MAIN
160 msg_Dbg( p_intf, "Starting familiar GTK+ interface" );
161 gtk_init( &i_args, &pp_args );
163 /* Initialize Gtk+ */
164 msg_Dbg( p_intf, "Starting familiar GTK+ interface thread" );
169 /* Create some useful widgets that will certainly be used */
171 add_pixmap_directory("share");
172 add_pixmap_directory("/usr/share/vlc");
174 /* Path for pixmaps under linupy 1.4 */
175 add_pixmap_directory("/usr/local/share/pixmaps/vlc");
177 /* Path for pixmaps under linupy 2.0 */
178 add_pixmap_directory("/usr/share/pixmaps/vlc");
180 p_intf->p_sys->p_window = create_familiar();
181 if (p_intf->p_sys->p_window == NULL)
183 msg_Err( p_intf, "unable to create familiar interface" );
185 gtk_widget_set_usize(p_intf->p_sys->p_window,
186 gdk_screen_width() , gdk_screen_height() - 30 );
188 /* Set the title of the main window */
189 gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
190 VOUT_TITLE " (Familiar Linux interface)");
192 p_intf->p_sys->p_notebook = GTK_NOTEBOOK( gtk_object_get_data(
193 GTK_OBJECT( p_intf->p_sys->p_window ), "notebook" ) );
194 p_intf->p_sys->p_mediabook = GTK_NOTEBOOK( gtk_object_get_data(
195 GTK_OBJECT( p_intf->p_sys->p_window ), "mediabook" ) );
197 /* Get the slider object */
198 p_intf->p_sys->p_slider = GTK_HSCALE( gtk_object_get_data(
199 GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) );
200 p_intf->p_sys->p_slider_label = GTK_LABEL( gtk_object_get_data(
201 GTK_OBJECT( p_intf->p_sys->p_window ), "slider_label" ) );
203 /* Connect the date display to the slider */
204 #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
205 GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
206 p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );
208 gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
209 GTK_SIGNAL_FUNC( E_(GtkDisplayDate) ), NULL );
210 p_intf->p_sys->f_adj_oldvalue = 0;
211 p_intf->p_sys->i_adj_oldvalue = 0;
214 p_intf->p_sys->p_clist = GTK_CLIST( gtk_object_get_data(
215 GTK_OBJECT( p_intf->p_sys->p_window ), "clistmedia" ) );
216 gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 2, FALSE);
217 gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 3, FALSE);
218 gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 4, FALSE);
219 gtk_clist_column_titles_show (GTK_CLIST (p_intf->p_sys->p_clist));
221 /* the playlist object */
222 p_intf->p_sys->p_clistplaylist = GTK_CLIST( gtk_object_get_data(
223 GTK_OBJECT( p_intf->p_sys->p_window ), "clistplaylist" ) );
225 p_intf->p_sys->p_mrlentry = GTK_ENTRY( gtk_object_get_data(
226 GTK_OBJECT( p_intf->p_sys->p_window ), "mrl_entry" ) );
228 /* Store p_intf to keep an eye on it */
229 gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
231 gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
234 /* Show the control window */
235 gtk_widget_show( p_intf->p_sys->p_window );
236 ReadDirectory(p_intf->p_sys->p_clist, ".");
238 /* update the playlist */
239 FamiliarRebuildCList( p_intf->p_sys->p_clistplaylist,
240 vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ));
243 msg_Dbg( p_intf, "Manage GTK keyboard events using threads" );
244 while( !p_intf->b_die )
248 /* Sleep to avoid using all CPU - since some interfaces need to
249 * access keyboard events, a 100ms delay is a good compromise */
251 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
252 msleep( INTF_IDLE_SLEEP );
258 msg_Dbg( p_intf, "Manage GTK keyboard events using timeouts" );
259 /* Sleep to avoid using all CPU - since some interfaces needs to access
260 * keyboard events, a 1000ms delay is a good compromise */
261 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
262 i_dummy = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, (GtkFunction)Manage, p_intf );
264 i_dummy = gtk_timeout_add( 1000, (GtkFunction)Manage, p_intf );
268 /* Remove the timeout */
269 gtk_timeout_remove( i_dummy );
272 gtk_object_destroy( GTK_OBJECT(p_intf->p_sys->p_window) );
278 /*****************************************************************************
279 * GtkAutoplayFile: Autoplay file depending on configuration settings
280 *****************************************************************************/
281 void GtkAutoPlayFile( vlc_object_t *p_this )
283 GtkWidget *cbautoplay;
284 intf_thread_t *p_intf;
286 vlc_list_t *p_list = vlc_list_find( p_this, VLC_OBJECT_INTF,
289 for( i_index = 0; i_index < p_list->i_count; i_index++ )
291 p_intf = (intf_thread_t *)p_list->p_values[i_index].p_object ;
293 if( strcmp( MODULE_STRING, p_intf->p_module->psz_object_name ) )
297 cbautoplay = GTK_WIDGET( gtk_object_get_data(
298 GTK_OBJECT( p_intf->p_sys->p_window ),
301 if( !config_GetInt( p_this, "familiar-autoplayfile" ) )
303 p_intf->p_sys->b_autoplayfile = VLC_FALSE;
307 p_intf->p_sys->b_autoplayfile = VLC_TRUE;
309 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( cbautoplay ),
310 p_intf->p_sys->b_autoplayfile );
312 vlc_list_release( p_list );
315 /* following functions are local */
317 /*****************************************************************************
318 * Manage: manage main thread messages
319 *****************************************************************************
320 * In this function, called approx. 10 times a second, we check what the
321 * main program wanted to tell us.
322 *****************************************************************************/
323 static int Manage( intf_thread_t *p_intf )
325 vlc_mutex_lock( &p_intf->change_lock );
327 /* Update the input */
328 if( p_intf->p_sys->p_input == NULL )
330 p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
333 else if( p_intf->p_sys->p_input->b_dead )
335 vlc_object_release( p_intf->p_sys->p_input );
336 p_intf->p_sys->p_input = NULL;
339 if( p_intf->p_sys->p_input )
341 input_thread_t *p_input = p_intf->p_sys->p_input;
343 vlc_mutex_lock( &p_input->stream.stream_lock );
344 if( !p_input->b_die )
346 /* New input or stream map change */
347 if( p_input->stream.b_changed )
349 playlist_t *p_playlist;
351 E_(GtkModeManage)( p_intf );
352 p_intf->p_sys->b_playing = 1;
354 /* update playlist interface */
355 p_playlist = (playlist_t *) vlc_object_find(
356 p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
357 if (p_playlist != NULL)
359 FamiliarRebuildCList( p_intf->p_sys->p_clistplaylist,
364 /* Manage the slider */
365 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
367 /* Manage the slider for CPU_CAPABILITY_FPU hardware */
368 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
370 float newvalue = p_intf->p_sys->p_adj->value;
372 #define p_area p_input->stream.p_selected_area
373 /* If the user hasn't touched the slider since the last time,
374 * then the input can safely change it */
375 if( newvalue == p_intf->p_sys->f_adj_oldvalue )
377 /* Update the value */
378 p_intf->p_sys->p_adj->value =
379 p_intf->p_sys->f_adj_oldvalue =
380 ( 100. * p_area->i_tell ) / p_area->i_size;
381 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
384 /* Otherwise, send message to the input if the user has
385 * finished dragging the slider */
386 else if( p_intf->p_sys->b_slider_free )
388 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
390 /* release the lock to be able to seek */
391 vlc_mutex_unlock( &p_input->stream.stream_lock );
392 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
393 vlc_mutex_lock( &p_input->stream.stream_lock );
395 /* Update the old value */
396 p_intf->p_sys->f_adj_oldvalue = newvalue;
403 /* Manage the slider without CPU_CAPABILITY_FPU hardware */
404 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
406 off_t newvalue = p_intf->p_sys->p_adj->value;
408 #define p_area p_input->stream.p_selected_area
409 /* If the user hasn't touched the slider since the last time,
410 * then the input can safely change it */
411 if( newvalue == p_intf->p_sys->i_adj_oldvalue )
413 /* Update the value */
414 p_intf->p_sys->p_adj->value =
415 p_intf->p_sys->i_adj_oldvalue =
416 ( 100 * p_area->i_tell ) / p_area->i_size;
417 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
420 /* Otherwise, send message to the input if the user has
421 * finished dragging the slider */
422 else if( p_intf->p_sys->b_slider_free )
424 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
426 /* release the lock to be able to seek */
427 vlc_mutex_unlock( &p_input->stream.stream_lock );
428 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
429 vlc_mutex_lock( &p_input->stream.stream_lock );
431 /* Update the old value */
432 p_intf->p_sys->i_adj_oldvalue = newvalue;
438 vlc_mutex_unlock( &p_input->stream.stream_lock );
440 else if( p_intf->p_sys->b_playing && !p_intf->b_die )
442 E_(GtkModeManage)( p_intf );
443 p_intf->p_sys->b_playing = 0;
446 #ifndef NEED_GTK_MAIN
449 vlc_mutex_unlock( &p_intf->change_lock );
451 /* Prepare to die, young Skywalker */
458 vlc_mutex_unlock( &p_intf->change_lock );
463 /*****************************************************************************
464 * GtkDisplayDate: display stream date
465 *****************************************************************************
466 * This function displays the current date related to the position in
467 * the stream. It is called whenever the slider changes its value.
468 * The lock has to be taken before you call the function.
469 *****************************************************************************/
470 void E_(GtkDisplayDate)( GtkAdjustment *p_adj )
472 intf_thread_t *p_intf;
474 p_intf = gtk_object_get_data( GTK_OBJECT( p_adj ), "p_intf" );
476 if( p_intf->p_sys->p_input )
478 #define p_area p_intf->p_sys->p_input->stream.p_selected_area
479 char psz_time[ OFFSETTOTIME_MAX_SIZE ];
481 gtk_label_set_text( GTK_LABEL( p_intf->p_sys->p_slider_label ),
482 input_OffsetToTime( p_intf->p_sys->p_input, psz_time,
483 ( p_area->i_size * p_adj->value ) / 100 ) );
488 /*****************************************************************************
489 * GtkModeManage: actualize the aspect of the interface whenever the input
491 *****************************************************************************
492 * The lock has to be taken before you call the function.
493 *****************************************************************************/
494 gint E_(GtkModeManage)( intf_thread_t * p_intf )
496 GtkWidget * p_slider;
497 vlc_bool_t b_control;
499 #define GETWIDGET( ptr, name ) GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( \
500 p_intf->p_sys->ptr ) , ( name ) ) )
502 p_slider = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
503 p_intf->p_sys->p_window ), "slider" ) );
504 gtk_widget_hide( GTK_WIDGET( p_slider ) );
506 /* controls unavailable */
509 /* show the box related to current input mode */
510 if( p_intf->p_sys->p_input )
512 /* initialize and show slider for seekable streams */
513 if( p_intf->p_sys->p_input->stream.b_seekable )
515 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
516 p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue = 0;
518 p_intf->p_sys->p_adj->value = p_intf->p_sys->i_adj_oldvalue = 0;
519 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
521 gtk_widget_show( GTK_WIDGET( p_slider ) );
524 /* control buttons for free pace streams */
525 b_control = p_intf->p_sys->p_input->stream.b_pace_control;
527 p_intf->p_sys->p_input->stream.b_changed = 0;
528 msg_Dbg( p_intf, "stream has changed, refreshing interface" );
531 /* set control items */
532 gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_rewind"), b_control );
533 gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_pause"), b_control );
534 gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_forward"), b_control );