1 /*****************************************************************************
2 * familiar.c : familiar plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: familiar.c,v 1.28 2003/01/27 17:41:01 ipkiss Exp $
7 * Authors: Jean-Paul Saman <jpsaman@wxs.nl>
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
28 #include <errno.h> /* ENOMEM */
29 #include <string.h> /* strerror() */
37 #ifdef HAVE_GPE_INIT_H
41 #include "callbacks.h"
42 #include "interface.h"
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Open ( vlc_object_t * );
50 static void Close ( vlc_object_t * );
51 static void Run ( intf_thread_t * );
53 void GtkAutoPlayFile ( vlc_object_t * );
54 static int Manage ( intf_thread_t *p_intf );
55 void E_(GtkDisplayDate) ( GtkAdjustment *p_adj );
56 gint E_(GtkModeManage) ( intf_thread_t * p_intf );
58 /*****************************************************************************
60 *****************************************************************************/
61 #define AUTOPLAYFILE_TEXT N_("autoplay selected file")
62 #define AUTOPLAYFILE_LONGTEXT N_("automatically play a file when selected in the "\
63 "file selection list")
65 /*****************************************************************************
67 *****************************************************************************/
69 add_category_hint( N_("Miscellaneous"), NULL );
70 add_bool( "familiar-autoplayfile", 1, GtkAutoPlayFile, AUTOPLAYFILE_TEXT, AUTOPLAYFILE_LONGTEXT );
71 set_description( _("Familiar Linux Gtk+ interface module") );
72 set_capability( "interface", 70 );
73 set_callbacks( Open, Close );
76 /*****************************************************************************
77 * Open: initialize and create window
78 *****************************************************************************/
79 static int Open( vlc_object_t *p_this )
81 intf_thread_t *p_intf = (intf_thread_t *)p_this;
83 /* Allocate instance and initialize some members */
84 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
85 if( p_intf->p_sys == NULL )
87 msg_Err( p_intf, "out of memory" );
92 msg_Dbg( p_intf, "Using gtk_main" );
93 p_intf->p_sys->p_gtk_main = module_Need( p_this, "gtk_main", "gtk" );
94 if( p_intf->p_sys->p_gtk_main == NULL )
96 free( p_intf->p_sys );
101 /* Initialize Gtk+ thread */
102 p_intf->p_sys->p_input = NULL;
104 p_intf->p_sys->b_autoplayfile = 1;
105 p_intf->p_sys->b_playing = 0;
106 p_intf->p_sys->b_slider_free = 1;
108 p_intf->pf_run = Run;
113 /*****************************************************************************
114 * Close: destroy interface window
115 *****************************************************************************/
116 static void Close( vlc_object_t *p_this )
118 intf_thread_t *p_intf = (intf_thread_t *)p_this;
120 if( p_intf->p_sys->p_input )
122 vlc_object_release( p_intf->p_sys->p_input );
126 msg_Dbg( p_intf, "Releasing gtk_main" );
127 module_Unneed( p_intf, p_intf->p_sys->p_gtk_main );
130 /* Destroy structure */
131 free( p_intf->p_sys );
134 /*****************************************************************************
136 *****************************************************************************
137 * this part of the interface is in a separate thread so that we can call
138 * gtk_main() from within it without annoying the rest of the program.
139 *****************************************************************************/
140 static void Run( intf_thread_t *p_intf )
142 #ifndef NEED_GTK_MAIN
143 /* gtk_init needs to know the command line. We don't care, so we
144 * give it an empty one */
145 char *p_args[] = { "" };
146 char **pp_args = p_args;
151 #ifdef HAVE_GPE_INIT_H
152 /* Initialize GPE interface */
153 msg_Dbg( p_intf, "Starting familiar GPE interface" );
154 if (gpe_application_init(&i_args, &pp_args) == FALSE)
158 #ifndef NEED_GTK_MAIN
159 msg_Dbg( p_intf, "Starting familiar GTK+ interface" );
160 gtk_init( &i_args, &pp_args );
162 /* Initialize Gtk+ */
163 msg_Dbg( p_intf, "Starting familiar GTK+ interface thread" );
168 /* Create some useful widgets that will certainly be used */
170 add_pixmap_directory("share");
171 add_pixmap_directory("/usr/share/vlc");
173 p_intf->p_sys->p_window = create_familiar();
174 if (p_intf->p_sys->p_window == NULL)
176 msg_Err( p_intf, "unable to create familiar interface" );
179 /* Set the title of the main window */
180 gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
181 VOUT_TITLE " (Familiar Linux interface)");
183 p_intf->p_sys->p_notebook = GTK_NOTEBOOK( gtk_object_get_data(
184 GTK_OBJECT( p_intf->p_sys->p_window ), "notebook" ) );
186 /* Get the slider object */
187 p_intf->p_sys->p_slider = GTK_HSCALE( gtk_object_get_data(
188 GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) );
189 p_intf->p_sys->p_slider_label = GTK_LABEL( gtk_object_get_data(
190 GTK_OBJECT( p_intf->p_sys->p_window ), "slider_label" ) );
192 /* Connect the date display to the slider */
193 #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
194 GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
195 p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );
197 gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
198 GTK_SIGNAL_FUNC( E_(GtkDisplayDate) ), NULL );
199 p_intf->p_sys->f_adj_oldvalue = 0;
200 p_intf->p_sys->i_adj_oldvalue = 0;
203 p_intf->p_sys->p_clist = GTK_CLIST( gtk_object_get_data(
204 GTK_OBJECT( p_intf->p_sys->p_window ), "clistmedia" ) );
205 gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 2, FALSE);
206 gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 3, FALSE);
207 gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 4, FALSE);
208 gtk_clist_column_titles_show (GTK_CLIST (p_intf->p_sys->p_clist));
210 /* Store p_intf to keep an eye on it */
211 gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
213 gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
216 /* Show the control window */
217 gtk_widget_show( p_intf->p_sys->p_window );
218 ReadDirectory(p_intf->p_sys->p_clist, "/mnt");
221 msg_Dbg( p_intf, "Manage GTK keyboard events using threads" );
222 while( !p_intf->b_die )
226 /* Sleep to avoid using all CPU - since some interfaces need to
227 * access keyboard events, a 100ms delay is a good compromise */
229 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
230 msleep( INTF_IDLE_SLEEP );
236 msg_Dbg( p_intf, "Manage GTK keyboard events using timeouts" );
237 /* Sleep to avoid using all CPU - since some interfaces needs to access
238 * keyboard events, a 1000ms delay is a good compromise */
239 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
240 i_dummy = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, (GtkFunction)Manage, p_intf );
242 i_dummy = gtk_timeout_add( 1000, (GtkFunction)Manage, p_intf );
246 /* Remove the timeout */
247 gtk_timeout_remove( i_dummy );
250 gtk_object_destroy( GTK_OBJECT(p_intf->p_sys->p_window) );
256 /*****************************************************************************
257 * GtkAutoplayFile: Autoplay file depending on configuration settings
258 *****************************************************************************/
259 void GtkAutoPlayFile( vlc_object_t *p_this )
261 GtkWidget *cbautoplay;
262 intf_thread_t *p_intf;
264 vlc_list_t *p_list = vlc_list_find( p_this, VLC_OBJECT_INTF,
267 for( i_index = 0; i_index < p_list->i_count; i_index++ )
269 p_intf = (intf_thread_t *)p_list->p_values[i_index].p_object ;
271 if( strcmp( MODULE_STRING, p_intf->p_module->psz_object_name ) )
275 cbautoplay = GTK_WIDGET( gtk_object_get_data(
276 GTK_OBJECT( p_intf->p_sys->p_window ),
279 if( !config_GetInt( p_this, "familiar-autoplayfile" ) )
281 p_intf->p_sys->b_autoplayfile = VLC_FALSE;
285 p_intf->p_sys->b_autoplayfile = VLC_TRUE;
287 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( cbautoplay ),
288 p_intf->p_sys->b_autoplayfile );
290 vlc_list_release( p_list );
293 /* following functions are local */
295 /*****************************************************************************
296 * Manage: manage main thread messages
297 *****************************************************************************
298 * In this function, called approx. 10 times a second, we check what the
299 * main program wanted to tell us.
300 *****************************************************************************/
301 static int Manage( intf_thread_t *p_intf )
303 vlc_mutex_lock( &p_intf->change_lock );
305 /* Update the input */
306 if( p_intf->p_sys->p_input == NULL )
308 p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
311 else if( p_intf->p_sys->p_input->b_dead )
313 vlc_object_release( p_intf->p_sys->p_input );
314 p_intf->p_sys->p_input = NULL;
317 if( p_intf->p_sys->p_input )
319 input_thread_t *p_input = p_intf->p_sys->p_input;
321 vlc_mutex_lock( &p_input->stream.stream_lock );
322 if( !p_input->b_die )
324 /* New input or stream map change */
325 if( p_input->stream.b_changed )
327 E_(GtkModeManage)( p_intf );
328 p_intf->p_sys->b_playing = 1;
331 /* Manage the slider */
332 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
334 /* Manage the slider for CPU_CAPABILITY_FPU hardware */
335 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
337 float newvalue = p_intf->p_sys->p_adj->value;
339 #define p_area p_input->stream.p_selected_area
340 /* If the user hasn't touched the slider since the last time,
341 * then the input can safely change it */
342 if( newvalue == p_intf->p_sys->f_adj_oldvalue )
344 /* Update the value */
345 p_intf->p_sys->p_adj->value =
346 p_intf->p_sys->f_adj_oldvalue =
347 ( 100. * p_area->i_tell ) / p_area->i_size;
348 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
351 /* Otherwise, send message to the input if the user has
352 * finished dragging the slider */
353 else if( p_intf->p_sys->b_slider_free )
355 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
357 /* release the lock to be able to seek */
358 vlc_mutex_unlock( &p_input->stream.stream_lock );
359 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
360 vlc_mutex_lock( &p_input->stream.stream_lock );
362 /* Update the old value */
363 p_intf->p_sys->f_adj_oldvalue = newvalue;
370 /* Manage the slider without CPU_CAPABILITY_FPU hardware */
371 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
373 off_t newvalue = p_intf->p_sys->p_adj->value;
375 #define p_area p_input->stream.p_selected_area
376 /* If the user hasn't touched the slider since the last time,
377 * then the input can safely change it */
378 if( newvalue == p_intf->p_sys->i_adj_oldvalue )
380 /* Update the value */
381 p_intf->p_sys->p_adj->value =
382 p_intf->p_sys->i_adj_oldvalue =
383 ( 100 * p_area->i_tell ) / p_area->i_size;
384 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
387 /* Otherwise, send message to the input if the user has
388 * finished dragging the slider */
389 else if( p_intf->p_sys->b_slider_free )
391 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
393 /* release the lock to be able to seek */
394 vlc_mutex_unlock( &p_input->stream.stream_lock );
395 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
396 vlc_mutex_lock( &p_input->stream.stream_lock );
398 /* Update the old value */
399 p_intf->p_sys->i_adj_oldvalue = newvalue;
405 vlc_mutex_unlock( &p_input->stream.stream_lock );
407 else if( p_intf->p_sys->b_playing && !p_intf->b_die )
409 E_(GtkModeManage)( p_intf );
410 p_intf->p_sys->b_playing = 0;
413 #ifndef NEED_GTK_MAIN
416 vlc_mutex_unlock( &p_intf->change_lock );
418 /* Prepare to die, young Skywalker */
425 vlc_mutex_unlock( &p_intf->change_lock );
430 /*****************************************************************************
431 * GtkDisplayDate: display stream date
432 *****************************************************************************
433 * This function displays the current date related to the position in
434 * the stream. It is called whenever the slider changes its value.
435 * The lock has to be taken before you call the function.
436 *****************************************************************************/
437 void E_(GtkDisplayDate)( GtkAdjustment *p_adj )
439 intf_thread_t *p_intf;
441 p_intf = gtk_object_get_data( GTK_OBJECT( p_adj ), "p_intf" );
443 if( p_intf->p_sys->p_input )
445 #define p_area p_intf->p_sys->p_input->stream.p_selected_area
446 char psz_time[ OFFSETTOTIME_MAX_SIZE ];
448 gtk_label_set_text( GTK_LABEL( p_intf->p_sys->p_slider_label ),
449 input_OffsetToTime( p_intf->p_sys->p_input, psz_time,
450 ( p_area->i_size * p_adj->value ) / 100 ) );
455 /*****************************************************************************
456 * GtkModeManage: actualize the aspect of the interface whenever the input
458 *****************************************************************************
459 * The lock has to be taken before you call the function.
460 *****************************************************************************/
461 gint E_(GtkModeManage)( intf_thread_t * p_intf )
463 GtkWidget * p_slider;
464 vlc_bool_t b_control;
466 #define GETWIDGET( ptr, name ) GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( \
467 p_intf->p_sys->ptr ) , ( name ) ) )
469 p_slider = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
470 p_intf->p_sys->p_window ), "slider" ) );
471 gtk_widget_hide( GTK_WIDGET( p_slider ) );
473 /* controls unavailable */
476 /* show the box related to current input mode */
477 if( p_intf->p_sys->p_input )
479 /* initialize and show slider for seekable streams */
480 if( p_intf->p_sys->p_input->stream.b_seekable )
482 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
483 p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue = 0;
485 p_intf->p_sys->p_adj->value = p_intf->p_sys->i_adj_oldvalue = 0;
486 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
488 gtk_widget_show( GTK_WIDGET( p_slider ) );
491 /* control buttons for free pace streams */
492 b_control = p_intf->p_sys->p_input->stream.b_pace_control;
494 p_intf->p_sys->p_input->stream.b_changed = 0;
495 msg_Dbg( p_intf, "stream has changed, refreshing interface" );
498 /* set control items */
499 gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_rewind"), b_control );
500 gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_pause"), b_control );
501 gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_forward"), b_control );