1 /*****************************************************************************
2 * pda.c : PDA Gtk2 plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: pda.c,v 1.4 2003/10/03 18:04:58 jpsaman 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 "pda_callbacks.h"
43 #include "pda_interface.h"
44 #include "pda_support.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( "pda-autoplayfile", 1, GtkAutoPlayFile, AUTOPLAYFILE_TEXT, AUTOPLAYFILE_LONGTEXT, VLC_TRUE );
72 set_description( _("PDA Linux Gtk2+ interface") );
73 set_capability( "interface", 70 );
74 set_callbacks( Open, Close );
75 add_shortcut( "pda" );
78 /*****************************************************************************
79 * Open: initialize and create window
80 *****************************************************************************/
81 static int Open( vlc_object_t *p_this )
83 intf_thread_t *p_intf = (intf_thread_t *)p_this;
85 /* Allocate instance and initialize some members */
86 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
87 if( p_intf->p_sys == NULL )
89 msg_Err( p_intf, "out of memory" );
94 msg_Dbg( p_intf, "Using gui-helper" );
95 p_intf->p_sys->p_gtk_main = module_Need( p_this, "gui-helper", "gtk2" );
96 if( p_intf->p_sys->p_gtk_main == NULL )
98 free( p_intf->p_sys );
103 /* Initialize Gtk+ thread */
104 p_intf->p_sys->p_input = NULL;
106 p_intf->p_sys->b_autoplayfile = 1;
107 p_intf->p_sys->b_playing = 0;
108 p_intf->p_sys->b_slider_free = 1;
110 p_intf->pf_run = Run;
115 /*****************************************************************************
116 * Close: destroy interface window
117 *****************************************************************************/
118 static void Close( vlc_object_t *p_this )
120 intf_thread_t *p_intf = (intf_thread_t *)p_this;
122 if( p_intf->p_sys->p_input )
124 vlc_object_release( p_intf->p_sys->p_input );
127 #ifdef NEED_GTK2_MAIN
128 msg_Dbg( p_intf, "Releasing gui-helper" );
129 module_Unneed( p_intf, p_intf->p_sys->p_gtk_main );
132 /* Destroy structure */
133 free( p_intf->p_sys );
136 /*****************************************************************************
138 *****************************************************************************
139 * this part of the interface is in a separate thread so that we can call
140 * gtk_main() from within it without annoying the rest of the program.
141 *****************************************************************************/
142 static void Run( intf_thread_t *p_intf )
144 #ifndef NEED_GTK2_MAIN
145 /* gtk_init needs to know the command line. We don't care, so we
146 * give it an empty one */
147 char *p_args[] = { "", NULL };
148 char **pp_args = p_args;
152 GtkCellRenderer *renderer = NULL;
154 #ifdef HAVE_GPE_INIT_H
155 /* Initialize GPE interface */
156 msg_Dbg( p_intf, "Starting pda GPE interface" );
157 if (gpe_application_init(&i_args, &pp_args) == FALSE)
161 # ifndef NEED_GTK2_MAIN
162 msg_Dbg( p_intf, "Starting pda GTK2+ interface" );
163 gtk_init( &i_args, &pp_args );
165 /* Initialize Gtk+ */
166 msg_Dbg( p_intf, "Starting pda GTK+ interface thread" );
171 /* Create some useful widgets that will certainly be used */
173 add_pixmap_directory("share");
174 add_pixmap_directory("/usr/share/vlc");
176 /* Path for pixmaps under linupy 1.4 */
177 add_pixmap_directory("/usr/local/share/pixmaps/vlc");
179 /* Path for pixmaps under linupy 2.0 */
180 add_pixmap_directory("/usr/share/pixmaps/vlc");
182 p_intf->p_sys->p_window = create_pda();
183 if (p_intf->p_sys->p_window == NULL)
185 msg_Err( p_intf, "unable to create pda interface" );
189 msg_Dbg( p_intf, "setting main window size ... " );
190 gtk_widget_set_usize(p_intf->p_sys->p_window,
191 gdk_screen_width() , gdk_screen_height() - 30 );
192 msg_Dbg( p_intf, "setting main window size ... done" );
195 /* Set the title of the main window */
196 gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
197 VOUT_TITLE " (PDA Linux interface)");
199 /* Get the notebook object */
200 p_intf->p_sys->p_notebook = GTK_NOTEBOOK( gtk_object_get_data(
201 GTK_OBJECT( p_intf->p_sys->p_window ), "notebook" ) );
202 p_intf->p_sys->p_mediabook = GTK_NOTEBOOK( gtk_object_get_data(
203 GTK_OBJECT( p_intf->p_sys->p_window ), "mediabook" ) );
205 /* Get the slider object */
206 p_intf->p_sys->p_slider = GTK_HSCALE( gtk_object_get_data(
207 GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) );
208 p_intf->p_sys->p_slider_label = GTK_LABEL( gtk_object_get_data(
209 GTK_OBJECT( p_intf->p_sys->p_window ), "slider_label" ) );
212 /* Connect the date display to the slider */
213 msg_Dbg( p_intf, "setting slider adjustment ... " );
214 #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
215 GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
216 p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );
218 gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
219 GTK_SIGNAL_FUNC( E_(GtkDisplayDate) ), NULL );
220 p_intf->p_sys->f_adj_oldvalue = 0;
221 p_intf->p_sys->i_adj_oldvalue = 0;
223 msg_Dbg( p_intf, "setting slider adjustment ... done" );
226 /* Get the GtkTreeView filelist object */
227 msg_Dbg(p_intf, "Getting GtkTreeView FileList" );
228 p_intf->p_sys->p_tvfile = NULL;
229 p_intf->p_sys->p_tvfile = (GtkTreeView *) lookup_widget( p_intf->p_sys->p_window,
231 if (NULL == p_intf->p_sys->p_tvfile)
232 msg_Err(p_intf, "Error obtaining pointer to File List");
233 /* Get new directory listing */
234 msg_Dbg(p_intf, "Populating GtkTreeView FileList" );
235 p_intf->p_sys->p_filelist = gtk_list_store_new (5,
236 G_TYPE_STRING, /* Filename */
237 G_TYPE_STRING, /* permissions */
238 G_TYPE_STRING, /* File size */
239 G_TYPE_STRING, /* Owner */
240 G_TYPE_STRING);/* Group */
241 ReadDirectory(p_intf, p_intf->p_sys->p_filelist, ".");
242 msg_Dbg(p_intf, "Showing GtkTreeView FileList" );
243 gtk_tree_view_set_model(p_intf->p_sys->p_tvfile, GTK_TREE_MODEL(p_intf->p_sys->p_filelist));
245 renderer = gtk_cell_renderer_text_new ();
246 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvfile, 0, _("Filename"), renderer, NULL);
247 renderer = gtk_cell_renderer_text_new ();
248 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvfile, 1, _("Permissions"), renderer, NULL);
249 renderer = gtk_cell_renderer_text_new ();
250 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvfile, 2, _("Size"), renderer, NULL);
251 renderer = gtk_cell_renderer_text_new ();
252 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvfile, 3, _("Owner"), renderer, NULL);
253 renderer = gtk_cell_renderer_text_new ();
254 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvfile, 4, _("Group"), renderer, NULL);
255 /* Column properties */
256 gtk_tree_view_set_headers_visible(p_intf->p_sys->p_tvfile, TRUE);
257 gtk_tree_view_columns_autosize(p_intf->p_sys->p_tvfile);
258 gtk_tree_view_set_headers_clickable(p_intf->p_sys->p_tvfile,TRUE);
260 /* Get the GtkTreeView playlist object */
261 msg_Dbg(p_intf, "Getting GtkTreeView PlayList" );
262 p_intf->p_sys->p_tvplaylist = NULL;
263 p_intf->p_sys->p_tvplaylist = (GtkTreeView *) lookup_widget( p_intf->p_sys->p_window,
265 if (NULL == p_intf->p_sys->p_tvplaylist)
266 msg_Err(p_intf, "Error obtaining pointer to Play List");
268 renderer = gtk_cell_renderer_text_new ();
269 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvplaylist, 0, _("Filename"), renderer, NULL);
270 renderer = gtk_cell_renderer_text_new ();
271 gtk_tree_view_insert_column_with_attributes(p_intf->p_sys->p_tvplaylist, 1, _("Time"), renderer, NULL);
272 /* update the playlist */
273 msg_Dbg(p_intf, "Populating GtkTreeView Playlist" );
274 p_intf->p_sys->p_playlist = gtk_list_store_new (2,
275 G_TYPE_STRING, /* Filename */
276 G_TYPE_STRING);/* Time */
277 PlaylistRebuildListStore( p_intf->p_sys->p_playlist, vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ));
278 msg_Dbg(p_intf, "Showing GtkTreeView Playlist" );
279 gtk_tree_view_set_model(p_intf->p_sys->p_tvplaylist, GTK_TREE_MODEL(p_intf->p_sys->p_playlist));
280 /* Column properties */
281 gtk_tree_view_set_headers_visible(p_intf->p_sys->p_tvplaylist, TRUE);
282 gtk_tree_view_columns_autosize(p_intf->p_sys->p_tvplaylist);
283 gtk_tree_view_set_headers_clickable(p_intf->p_sys->p_tvplaylist, TRUE);
285 p_intf->p_sys->p_mrlentry = GTK_ENTRY( gtk_object_get_data(
286 GTK_OBJECT( p_intf->p_sys->p_window ), "mrl_entry" ) );
289 /* Store p_intf to keep an eye on it */
290 msg_Dbg( p_intf, "trying to store p_intf pointer ... " );
291 gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
293 gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
295 msg_Dbg( p_intf, "trying to store p_intf pointer ... done" );
298 /* Show the control window */
299 gtk_widget_show( p_intf->p_sys->p_window );
301 #ifdef NEED_GTK2_MAIN
302 msg_Dbg( p_intf, "Manage GTK keyboard events using threads" );
303 while( !p_intf->b_die )
307 /* Sleep to avoid using all CPU - since some interfaces need to
308 * access keyboard events, a 100ms delay is a good compromise */
310 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
311 msleep( INTF_IDLE_SLEEP );
317 msg_Dbg( p_intf, "Manage GTK keyboard events using timeouts" );
318 /* Sleep to avoid using all CPU - since some interfaces needs to access
319 * keyboard events, a 1000ms delay is a good compromise */
320 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
321 i_dummy = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, (GtkFunction)Manage, p_intf );
323 i_dummy = gtk_timeout_add( 1000, (GtkFunction)Manage, p_intf );
327 /* Remove the timeout */
328 gtk_timeout_remove( i_dummy );
331 gtk_object_destroy( GTK_OBJECT(p_intf->p_sys->p_window) );
332 #ifdef NEED_GTK2_MAIN
337 /*****************************************************************************
338 * GtkAutoplayFile: Autoplay file depending on configuration settings
339 *****************************************************************************/
340 void GtkAutoPlayFile( vlc_object_t *p_this )
342 GtkWidget *cbautoplay;
343 intf_thread_t *p_intf;
345 vlc_list_t *p_list = vlc_list_find( p_this, VLC_OBJECT_INTF,
348 for( i_index = 0; i_index < p_list->i_count; i_index++ )
350 p_intf = (intf_thread_t *)p_list->p_values[i_index].p_object ;
352 if( strcmp( MODULE_STRING, p_intf->p_module->psz_object_name ) )
356 cbautoplay = GTK_WIDGET( gtk_object_get_data(
357 GTK_OBJECT( p_intf->p_sys->p_window ),
360 if( !config_GetInt( p_this, "pda-autoplayfile" ) )
362 p_intf->p_sys->b_autoplayfile = VLC_FALSE;
366 p_intf->p_sys->b_autoplayfile = VLC_TRUE;
368 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( cbautoplay ),
369 p_intf->p_sys->b_autoplayfile );
371 vlc_list_release( p_list );
374 /* following functions are local */
376 /*****************************************************************************
377 * Manage: manage main thread messages
378 *****************************************************************************
379 * In this function, called approx. 10 times a second, we check what the
380 * main program wanted to tell us.
381 *****************************************************************************/
382 static int Manage( intf_thread_t *p_intf )
384 GtkListStore *p_liststore;
385 vlc_mutex_lock( &p_intf->change_lock );
387 /* Update the input */
388 if( p_intf->p_sys->p_input == NULL )
390 p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
393 else if( p_intf->p_sys->p_input->b_dead )
395 vlc_object_release( p_intf->p_sys->p_input );
396 p_intf->p_sys->p_input = NULL;
399 if( p_intf->p_sys->p_input )
401 input_thread_t *p_input = p_intf->p_sys->p_input;
403 vlc_mutex_lock( &p_input->stream.stream_lock );
404 if( !p_input->b_die )
406 /* New input or stream map change */
407 if( p_input->stream.b_changed )
409 playlist_t *p_playlist;
411 E_(GtkModeManage)( p_intf );
412 p_intf->p_sys->b_playing = 1;
414 /* update playlist interface */
415 p_playlist = (playlist_t *) vlc_object_find(
416 p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
417 if (p_playlist != NULL)
419 msg_Dbg(p_intf, "Manage: Populating GtkTreeView Playlist" );
420 p_liststore = gtk_list_store_new (2,
423 PlaylistRebuildListStore(p_liststore, p_playlist);
424 msg_Dbg(p_intf, "Manage: Updating GtkTreeView Playlist" );
425 gtk_tree_view_set_model(p_intf->p_sys->p_tvplaylist, (GtkTreeModel*) p_liststore);
429 /* Manage the slider */
430 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
432 /* Manage the slider for CPU_CAPABILITY_FPU hardware */
433 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
435 float newvalue = p_intf->p_sys->p_adj->value;
437 #define p_area p_input->stream.p_selected_area
438 /* If the user hasn't touched the slider since the last time,
439 * then the input can safely change it */
440 if( newvalue == p_intf->p_sys->f_adj_oldvalue )
442 /* Update the value */
443 p_intf->p_sys->p_adj->value =
444 p_intf->p_sys->f_adj_oldvalue =
445 ( 100. * p_area->i_tell ) / p_area->i_size;
446 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
449 /* Otherwise, send message to the input if the user has
450 * finished dragging the slider */
451 else if( p_intf->p_sys->b_slider_free )
453 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
455 /* release the lock to be able to seek */
456 vlc_mutex_unlock( &p_input->stream.stream_lock );
457 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
458 vlc_mutex_lock( &p_input->stream.stream_lock );
460 /* Update the old value */
461 p_intf->p_sys->f_adj_oldvalue = newvalue;
468 /* Manage the slider without CPU_CAPABILITY_FPU hardware */
469 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
471 off_t newvalue = p_intf->p_sys->p_adj->value;
473 #define p_area p_input->stream.p_selected_area
474 /* If the user hasn't touched the slider since the last time,
475 * then the input can safely change it */
476 if( newvalue == p_intf->p_sys->i_adj_oldvalue )
478 /* Update the value */
479 p_intf->p_sys->p_adj->value =
480 p_intf->p_sys->i_adj_oldvalue =
481 ( 100 * p_area->i_tell ) / p_area->i_size;
482 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
485 /* Otherwise, send message to the input if the user has
486 * finished dragging the slider */
487 else if( p_intf->p_sys->b_slider_free )
489 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
491 /* release the lock to be able to seek */
492 vlc_mutex_unlock( &p_input->stream.stream_lock );
493 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
494 vlc_mutex_lock( &p_input->stream.stream_lock );
496 /* Update the old value */
497 p_intf->p_sys->i_adj_oldvalue = newvalue;
503 vlc_mutex_unlock( &p_input->stream.stream_lock );
505 else if( p_intf->p_sys->b_playing && !p_intf->b_die )
507 E_(GtkModeManage)( p_intf );
508 p_intf->p_sys->b_playing = 0;
511 #ifndef NEED_GTK2_MAIN
514 vlc_mutex_unlock( &p_intf->change_lock );
516 /* Prepare to die, young Skywalker */
523 vlc_mutex_unlock( &p_intf->change_lock );
528 /*****************************************************************************
529 * GtkDisplayDate: display stream date
530 *****************************************************************************
531 * This function displays the current date related to the position in
532 * the stream. It is called whenever the slider changes its value.
533 * The lock has to be taken before you call the function.
534 *****************************************************************************/
535 void E_(GtkDisplayDate)( GtkAdjustment *p_adj )
537 intf_thread_t *p_intf;
539 p_intf = gtk_object_get_data( GTK_OBJECT( p_adj ), "p_intf" );
541 if( p_intf->p_sys->p_input )
543 #define p_area p_intf->p_sys->p_input->stream.p_selected_area
544 char psz_time[ OFFSETTOTIME_MAX_SIZE ];
546 gtk_label_set_text( GTK_LABEL( p_intf->p_sys->p_slider_label ),
547 input_OffsetToTime( p_intf->p_sys->p_input, psz_time,
548 ( p_area->i_size * p_adj->value ) / 100 ) );
553 /*****************************************************************************
554 * GtkModeManage: actualize the aspect of the interface whenever the input
556 *****************************************************************************
557 * The lock has to be taken before you call the function.
558 *****************************************************************************/
559 gint E_(GtkModeManage)( intf_thread_t * p_intf )
561 GtkWidget * p_slider;
562 vlc_bool_t b_control;
564 #define GETWIDGET( ptr, name ) GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( \
565 p_intf->p_sys->ptr ) , ( name ) ) )
567 p_slider = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
568 p_intf->p_sys->p_window ), "slider" ) );
569 gtk_widget_hide( GTK_WIDGET( p_slider ) );
571 /* controls unavailable */
574 /* show the box related to current input mode */
575 if( p_intf->p_sys->p_input )
577 /* initialize and show slider for seekable streams */
578 if( p_intf->p_sys->p_input->stream.b_seekable )
580 if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
581 p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue = 0;
583 p_intf->p_sys->p_adj->value = p_intf->p_sys->i_adj_oldvalue = 0;
584 gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
586 gtk_widget_show( GTK_WIDGET( p_slider ) );
589 /* control buttons for free pace streams */
590 b_control = p_intf->p_sys->p_input->stream.b_pace_control;
592 p_intf->p_sys->p_input->stream.b_changed = 0;
593 msg_Dbg( p_intf, "stream has changed, refreshing interface" );
596 /* set control items */
597 gtk_widget_set_sensitive( GETWIDGET(p_window, "tbRewind"), b_control );
598 gtk_widget_set_sensitive( GETWIDGET(p_window, "tbPause"), b_control );
599 gtk_widget_set_sensitive( GETWIDGET(p_window, "tbForward"), b_control );