]> git.sesse.net Git - vlc/blob - modules/gui/pda/pda.c
Gtk2 port of the familiar Linux interface. The name is also changed to PDA so it...
[vlc] / modules / gui / pda / pda.c
1 /*****************************************************************************
2  * pda.c : PDA Gtk2 plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: pda.c,v 1.2 2003/07/27 21:35:51 jpsaman Exp $
6  *
7  * Authors: Jean-Paul Saman <jpsaman@wxs.nl>
8  *          Marc Ariberti <marcari@videolan.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <string.h>                                            /* strerror() */
31 #include <stdio.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/intf.h>
35
36 #include <gtk/gtk.h>
37
38 #ifdef HAVE_GPE_INIT_H
39 #include <gpe/init.h>
40 #endif
41
42 #include "callbacks.h"
43 #include "interface.h"
44 #include "support.h"
45 #include "pda.h"
46
47 /*****************************************************************************
48  * Local prototypes.
49  *****************************************************************************/
50 static int  Open         ( vlc_object_t * );
51 static void Close        ( vlc_object_t * );
52 static void Run          ( intf_thread_t * );
53
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 );
58
59 /*****************************************************************************
60  * Module descriptor
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")
65
66 /*****************************************************************************
67  * Module descriptor
68  *****************************************************************************/
69 vlc_module_begin();
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" );
76 vlc_module_end();
77
78 /*****************************************************************************
79  * Open: initialize and create window
80  *****************************************************************************/
81 static int Open( vlc_object_t *p_this )
82 {
83     intf_thread_t *p_intf = (intf_thread_t *)p_this;
84
85     /* Allocate instance and initialize some members */
86     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
87     if( p_intf->p_sys == NULL )
88     {
89         msg_Err( p_intf, "out of memory" );
90         return VLC_ENOMEM;
91     }
92
93 #ifdef NEED_GTK_MAIN
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 )
97     {
98         free( p_intf->p_sys );
99         return VLC_ENOMOD;
100     }
101 #endif
102
103     /* Initialize Gtk+ thread */
104     p_intf->p_sys->p_input = NULL;
105
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;
109
110     p_intf->pf_run = Run;
111
112     return VLC_SUCCESS;
113 }
114
115 /*****************************************************************************
116  * Close: destroy interface window
117  *****************************************************************************/
118 static void Close( vlc_object_t *p_this )
119 {
120     intf_thread_t *p_intf = (intf_thread_t *)p_this;
121
122     if( p_intf->p_sys->p_input )
123     {
124         vlc_object_release( p_intf->p_sys->p_input );
125     }
126
127 #ifdef NEED_GTK_MAIN
128     msg_Dbg( p_intf, "Releasing gui-helper" );
129     module_Unneed( p_intf, p_intf->p_sys->p_gtk_main );
130 #endif
131
132     /* Destroy structure */
133     free( p_intf->p_sys );
134 }
135
136 /*****************************************************************************
137  * Run: Gtk+ thread
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 )
143 {
144 #ifndef NEED_GTK_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;
149     int    i_args   = 1;
150     int    i_dummy;
151 #endif
152
153 #ifdef HAVE_GPE_INIT_H
154     /* Initialize GPE interface */
155     msg_Dbg( p_intf, "Starting pda GPE interface" );
156     if (gpe_application_init(&i_args, &pp_args) == FALSE)
157         exit (1);
158 #else
159     gtk_set_locale ();
160 #   ifndef NEED_GTK_MAIN
161     msg_Dbg( p_intf, "Starting pda GTK2+ interface" );
162     gtk_init( &i_args, &pp_args );
163 #   else
164     /* Initialize Gtk+ */
165     msg_Dbg( p_intf, "Starting pda GTK+ interface thread" );
166     gdk_threads_enter();
167 #   endif
168 #endif
169
170     /* Create some useful widgets that will certainly be used */
171 // FIXME: magic path
172     add_pixmap_directory("share");
173     add_pixmap_directory("/usr/share/vlc");
174
175     /* Path for pixmaps under linupy 1.4 */
176     add_pixmap_directory("/usr/local/share/pixmaps/vlc");
177
178     /* Path for pixmaps under linupy 2.0 */
179     add_pixmap_directory("/usr/share/pixmaps/vlc");
180
181     p_intf->p_sys->p_window = create_pda();
182     if (p_intf->p_sys->p_window == NULL)
183     {
184         msg_Err( p_intf, "unable to create pda interface" );
185     }
186     gtk_widget_set_usize(p_intf->p_sys->p_window, 
187                         gdk_screen_width() , gdk_screen_height() - 30 );
188
189     /* Set the title of the main window */
190     gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
191                           VOUT_TITLE " (pda Linux interface)");
192
193     p_intf->p_sys->p_notebook = GTK_NOTEBOOK( gtk_object_get_data(
194         GTK_OBJECT( p_intf->p_sys->p_window ), "notebook" ) );
195     p_intf->p_sys->p_mediabook = GTK_NOTEBOOK( gtk_object_get_data(
196         GTK_OBJECT( p_intf->p_sys->p_window ), "mediabook" ) );
197
198     /* Get the slider object */
199     p_intf->p_sys->p_slider = GTK_HSCALE( gtk_object_get_data(
200         GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) );
201     p_intf->p_sys->p_slider_label = GTK_LABEL( gtk_object_get_data(
202         GTK_OBJECT( p_intf->p_sys->p_window ), "slider_label" ) );
203
204     /* Connect the date display to the slider */
205 #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
206                          GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
207     p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );
208
209     gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
210                          GTK_SIGNAL_FUNC( E_(GtkDisplayDate) ), NULL );
211     p_intf->p_sys->f_adj_oldvalue = 0;
212     p_intf->p_sys->i_adj_oldvalue = 0;
213 #undef P_SLIDER
214
215     p_intf->p_sys->p_clist = GTK_CLIST( gtk_object_get_data(
216         GTK_OBJECT( p_intf->p_sys->p_window ), "clistmedia" ) );
217     gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 2, FALSE);
218     gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 3, FALSE);
219     gtk_clist_set_column_visibility (GTK_CLIST (p_intf->p_sys->p_clist), 4, FALSE);
220     gtk_clist_column_titles_show (GTK_CLIST (p_intf->p_sys->p_clist));
221
222     /* the playlist object */
223     p_intf->p_sys->p_clistplaylist = GTK_CLIST( gtk_object_get_data(
224         GTK_OBJECT( p_intf->p_sys->p_window ), "clistplaylist" ) );
225     
226     p_intf->p_sys->p_mrlentry = GTK_ENTRY( gtk_object_get_data(
227         GTK_OBJECT( p_intf->p_sys->p_window ), "mrl_entry" ) );
228
229     /* Store p_intf to keep an eye on it */
230     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
231                          "p_intf", p_intf );
232     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
233                          "p_intf", p_intf );
234     
235     /* Show the control window */
236     gtk_widget_show( p_intf->p_sys->p_window );
237     ReadDirectory(p_intf->p_sys->p_clist, ".");
238
239     /* update the playlist */
240     PDARebuildCList( p_intf->p_sys->p_clistplaylist, 
241         vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ));
242     
243 #ifdef NEED_GTK_MAIN
244     msg_Dbg( p_intf, "Manage GTK keyboard events using threads" );
245     while( !p_intf->b_die )
246     {
247         Manage( p_intf );
248
249         /* Sleep to avoid using all CPU - since some interfaces need to
250          * access keyboard events, a 100ms delay is a good compromise */
251         gdk_threads_leave();
252         if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
253             msleep( INTF_IDLE_SLEEP );
254         else
255             msleep( 1000 );
256         gdk_threads_enter();
257     }
258 #else
259     msg_Dbg( p_intf, "Manage GTK keyboard events using timeouts" );
260     /* Sleep to avoid using all CPU - since some interfaces needs to access
261      * keyboard events, a 1000ms delay is a good compromise */
262     if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
263         i_dummy = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, (GtkFunction)Manage, p_intf );
264     else
265         i_dummy = gtk_timeout_add( 1000, (GtkFunction)Manage, p_intf );
266
267     /* Enter Gtk mode */
268     gtk_main();
269     /* Remove the timeout */
270     gtk_timeout_remove( i_dummy );
271 #endif
272
273     gtk_object_destroy( GTK_OBJECT(p_intf->p_sys->p_window) );
274 #ifdef NEED_GTK_MAIN
275     gdk_threads_leave();
276 #endif
277 }
278
279 /*****************************************************************************
280  * GtkAutoplayFile: Autoplay file depending on configuration settings
281  *****************************************************************************/
282 void GtkAutoPlayFile( vlc_object_t *p_this )
283 {
284     GtkWidget *cbautoplay;
285     intf_thread_t *p_intf;
286     int i_index;
287     vlc_list_t *p_list = vlc_list_find( p_this, VLC_OBJECT_INTF,
288                                         FIND_ANYWHERE );
289
290     for( i_index = 0; i_index < p_list->i_count; i_index++ )
291     {
292         p_intf = (intf_thread_t *)p_list->p_values[i_index].p_object ;
293
294         if( strcmp( MODULE_STRING, p_intf->p_module->psz_object_name ) )
295         {
296             continue;
297         }
298         cbautoplay = GTK_WIDGET( gtk_object_get_data(
299                             GTK_OBJECT( p_intf->p_sys->p_window ),
300                             "cbautoplay" ) );
301
302         if( !config_GetInt( p_this, "pda-autoplayfile" ) )
303         {
304             p_intf->p_sys->b_autoplayfile = VLC_FALSE;
305         }
306         else
307         {
308             p_intf->p_sys->b_autoplayfile = VLC_TRUE;
309         }
310         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( cbautoplay ),
311                                       p_intf->p_sys->b_autoplayfile );
312     }
313     vlc_list_release( p_list );
314 }
315
316 /* following functions are local */
317
318 /*****************************************************************************
319  * Manage: manage main thread messages
320  *****************************************************************************
321  * In this function, called approx. 10 times a second, we check what the
322  * main program wanted to tell us.
323  *****************************************************************************/
324 static int Manage( intf_thread_t *p_intf )
325 {
326     vlc_mutex_lock( &p_intf->change_lock );
327
328     /* Update the input */
329     if( p_intf->p_sys->p_input == NULL )
330     {
331         p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
332                                                           FIND_ANYWHERE );
333     }
334     else if( p_intf->p_sys->p_input->b_dead )
335     {
336         vlc_object_release( p_intf->p_sys->p_input );
337         p_intf->p_sys->p_input = NULL;
338     }
339
340     if( p_intf->p_sys->p_input )
341     {
342         input_thread_t *p_input = p_intf->p_sys->p_input;
343
344         vlc_mutex_lock( &p_input->stream.stream_lock );
345         if( !p_input->b_die )
346         {
347             /* New input or stream map change */
348             if( p_input->stream.b_changed )
349             {
350                 playlist_t *p_playlist;
351
352                 E_(GtkModeManage)( p_intf );
353                 p_intf->p_sys->b_playing = 1;
354
355                 /* update playlist interface */
356                 p_playlist = (playlist_t *) vlc_object_find( 
357                         p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
358                 if (p_playlist != NULL)
359                 {
360                     PDARebuildCList( p_intf->p_sys->p_clistplaylist, 
361                                           p_playlist );
362                 }
363             }
364
365             /* Manage the slider */
366             if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
367             {
368                 /* Manage the slider for CPU_CAPABILITY_FPU hardware */ 
369                 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
370                 {
371                     float newvalue = p_intf->p_sys->p_adj->value;
372
373 #define p_area p_input->stream.p_selected_area
374                     /* If the user hasn't touched the slider since the last time,
375                      * then the input can safely change it */
376                     if( newvalue == p_intf->p_sys->f_adj_oldvalue )
377                     {
378                         /* Update the value */
379                         p_intf->p_sys->p_adj->value =
380                         p_intf->p_sys->f_adj_oldvalue =
381                             ( 100. * p_area->i_tell ) / p_area->i_size;
382                         gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
383                                                  "value_changed" );
384                     }
385                     /* Otherwise, send message to the input if the user has
386                      * finished dragging the slider */
387                     else if( p_intf->p_sys->b_slider_free )
388                     {
389                         off_t i_seek = ( newvalue * p_area->i_size ) / 100;
390
391                         /* release the lock to be able to seek */
392                         vlc_mutex_unlock( &p_input->stream.stream_lock );
393                         input_Seek( p_input, i_seek, INPUT_SEEK_SET );
394                         vlc_mutex_lock( &p_input->stream.stream_lock );
395
396                         /* Update the old value */
397                         p_intf->p_sys->f_adj_oldvalue = newvalue;
398                     }
399 #undef p_area
400                 }
401             }
402             else
403             {
404                 /* Manage the slider without CPU_CAPABILITY_FPU hardware */
405                 if( p_input->stream.b_seekable && p_intf->p_sys->b_playing )
406                 {
407                     off_t newvalue = p_intf->p_sys->p_adj->value;
408
409 #define p_area p_input->stream.p_selected_area
410                     /* If the user hasn't touched the slider since the last time,
411                      * then the input can safely change it */
412                     if( newvalue == p_intf->p_sys->i_adj_oldvalue )
413                     {
414                         /* Update the value */
415                         p_intf->p_sys->p_adj->value =
416                         p_intf->p_sys->i_adj_oldvalue =
417                             ( 100 * p_area->i_tell ) / p_area->i_size;
418                         gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
419                                                  "value_changed" );
420                     }
421                     /* Otherwise, send message to the input if the user has
422                      * finished dragging the slider */
423                     else if( p_intf->p_sys->b_slider_free )
424                     {
425                         off_t i_seek = ( newvalue * p_area->i_size ) / 100;
426
427                         /* release the lock to be able to seek */
428                         vlc_mutex_unlock( &p_input->stream.stream_lock );
429                         input_Seek( p_input, i_seek, INPUT_SEEK_SET );
430                         vlc_mutex_lock( &p_input->stream.stream_lock );
431
432                         /* Update the old value */
433                         p_intf->p_sys->i_adj_oldvalue = newvalue;
434                     }
435 #undef p_area
436                 }
437             }
438         }
439         vlc_mutex_unlock( &p_input->stream.stream_lock );
440     }
441     else if( p_intf->p_sys->b_playing && !p_intf->b_die )
442     {
443         E_(GtkModeManage)( p_intf );
444         p_intf->p_sys->b_playing = 0;
445     }
446
447 #ifndef NEED_GTK_MAIN
448     if( p_intf->b_die )
449     {
450         vlc_mutex_unlock( &p_intf->change_lock );
451
452         /* Prepare to die, young Skywalker */
453         gtk_main_quit();
454
455         return FALSE;
456     }
457 #endif
458
459     vlc_mutex_unlock( &p_intf->change_lock );
460
461     return TRUE;
462 }
463
464 /*****************************************************************************
465  * GtkDisplayDate: display stream date
466  *****************************************************************************
467  * This function displays the current date related to the position in
468  * the stream. It is called whenever the slider changes its value.
469  * The lock has to be taken before you call the function.
470  *****************************************************************************/
471 void E_(GtkDisplayDate)( GtkAdjustment *p_adj )
472 {
473     intf_thread_t *p_intf;
474
475     p_intf = gtk_object_get_data( GTK_OBJECT( p_adj ), "p_intf" );
476
477     if( p_intf->p_sys->p_input )
478     {
479 #define p_area p_intf->p_sys->p_input->stream.p_selected_area
480         char psz_time[ OFFSETTOTIME_MAX_SIZE ];
481
482         gtk_label_set_text( GTK_LABEL( p_intf->p_sys->p_slider_label ),
483                         input_OffsetToTime( p_intf->p_sys->p_input, psz_time,
484                                    ( p_area->i_size * p_adj->value ) / 100 ) );
485 #undef p_area
486      }
487 }
488
489 /*****************************************************************************
490  * GtkModeManage: actualize the aspect of the interface whenever the input
491  *                changes.
492  *****************************************************************************
493  * The lock has to be taken before you call the function.
494  *****************************************************************************/
495 gint E_(GtkModeManage)( intf_thread_t * p_intf )
496 {
497     GtkWidget *     p_slider;
498     vlc_bool_t      b_control;
499
500 #define GETWIDGET( ptr, name ) GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( \
501                            p_intf->p_sys->ptr ) , ( name ) ) )
502     /* hide slider */
503     p_slider = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
504                            p_intf->p_sys->p_window ), "slider" ) );
505     gtk_widget_hide( GTK_WIDGET( p_slider ) );
506
507     /* controls unavailable */
508     b_control = 0;
509
510     /* show the box related to current input mode */
511     if( p_intf->p_sys->p_input )
512     {
513         /* initialize and show slider for seekable streams */
514         if( p_intf->p_sys->p_input->stream.b_seekable )
515         {
516             if (p_intf->p_libvlc->i_cpu & CPU_CAPABILITY_FPU)
517                 p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue = 0;
518             else
519                 p_intf->p_sys->p_adj->value = p_intf->p_sys->i_adj_oldvalue = 0;
520             gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
521                                      "value_changed" );
522             gtk_widget_show( GTK_WIDGET( p_slider ) );
523         }
524
525         /* control buttons for free pace streams */
526         b_control = p_intf->p_sys->p_input->stream.b_pace_control;
527
528         p_intf->p_sys->p_input->stream.b_changed = 0;
529         msg_Dbg( p_intf, "stream has changed, refreshing interface" );
530     }
531
532     /* set control items */
533     gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_rewind"), b_control );
534     gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_pause"), b_control );
535     gtk_widget_set_sensitive( GETWIDGET(p_window, "toolbar_forward"), b_control );
536
537 #undef GETWIDGET
538     return TRUE;
539 }
540