]> git.sesse.net Git - vlc/blob - plugins/gtk/gnome.c
11f3d604362a3d4f6888cafd1ea0e057afaca063
[vlc] / plugins / gtk / gnome.c
1 /*****************************************************************************
2  * gnome.c : Gnome plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  * $Id: gnome.c,v 1.20 2002/05/04 02:05:03 lool Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
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.
13  * 
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <string.h>                                            /* strerror() */
30 #include <stdio.h>
31
32 #include <videolan/vlc.h>
33
34 #include <gnome.h>
35
36 #include "stream_control.h"
37 #include "input_ext-intf.h"
38
39 #include "interface.h"
40 #include "intf_playlist.h"
41
42 #include "video.h"
43 #include "video_output.h"
44
45 #include "gnome_callbacks.h"
46 #include "gnome_interface.h"
47 #include "gnome_support.h"
48 #include "gtk_display.h"
49 #include "gtk_common.h"
50
51 /*****************************************************************************
52  * Local prototypes.
53  *****************************************************************************/
54 static void intf_getfunctions( function_list_t * p_function_list );
55 static int  intf_Open        ( intf_thread_t *p_intf );
56 static void intf_Close       ( intf_thread_t *p_intf );
57 static void intf_Run         ( intf_thread_t *p_intf );
58
59 static gint GnomeManage      ( gpointer p_data );
60
61 /*****************************************************************************
62  * Building configuration tree
63  *****************************************************************************/
64 #define TOOLTIPS_TEXT N_("hide tooltips")
65 #define TOOLTIPS_LONGTEXT N_("Do not show tooltips for configuration options.")
66 #define TOOLBAR_TEXT N_("hide text on toolbar buttons")
67 #define TOOLBAR_LONGTEXT N_("Do not show the text below icons on the toolbar.")
68
69 #define PREFS_MAXH_TEXT N_("maximum height for the configuration windows")
70 #define PREFS_MAXH_LONGTEXT N_( \
71     "You can set the maximum height that the configuration windows in the " \
72     "preferences menu will occupy.")
73
74 MODULE_CONFIG_START
75     ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
76     ADD_BOOL    ( "gnome-notooltips", GtkHideTooltips, TOOLTIPS_TEXT,
77                   TOOLTIPS_LONGTEXT )
78     ADD_BOOL    ( "gnome-notoolbartext", GtkHideToolbarText, TOOLBAR_TEXT,
79                   TOOLBAR_LONGTEXT )
80     ADD_INTEGER ( "gnome-prefs-maxh", 480, NULL, PREFS_MAXH_TEXT,
81                   PREFS_MAXH_LONGTEXT )
82 MODULE_CONFIG_STOP
83
84 MODULE_INIT_START
85     SET_DESCRIPTION( _("Gnome interface module") )
86 #ifndef WIN32
87     if( getenv( "DISPLAY" ) == NULL )
88     {
89         ADD_CAPABILITY( INTF, 15 )
90     }
91     else
92 #endif
93     {
94         ADD_CAPABILITY( INTF, 100 )
95     }
96     ADD_SHORTCUT( "gnome" )
97     ADD_PROGRAM( "gnome-vlc" )
98 MODULE_INIT_STOP
99
100 MODULE_ACTIVATE_START
101     intf_getfunctions( &p_module->p_functions->intf );
102 MODULE_ACTIVATE_STOP
103
104 MODULE_DEACTIVATE_START
105 MODULE_DEACTIVATE_STOP
106
107 /*****************************************************************************
108  * g_atexit: kludge to avoid the Gnome thread to segfault at exit
109  *****************************************************************************
110  * gtk_init() makes several calls to g_atexit() which calls atexit() to
111  * register tidying callbacks to be called at program exit. Since the Gnome
112  * plugin is likely to be unloaded at program exit, we have to export this
113  * symbol to intercept the g_atexit() calls. Talk about crude hack.
114  *****************************************************************************/
115 void g_atexit( GVoidFunc func )
116 {
117     intf_thread_t *p_intf = p_main->p_intf;
118     int i_dummy;
119
120     for( i_dummy = 0;
121          i_dummy < MAX_ATEXIT && p_intf->p_sys->pf_callback[i_dummy] != NULL;
122          i_dummy++ )
123     {
124         ;
125     }
126
127     if( i_dummy >= MAX_ATEXIT - 1 )
128     {
129         intf_ErrMsg( "intf error: too many atexit() callbacks to register" );
130         return;
131     }
132
133     p_intf->p_sys->pf_callback[i_dummy]     = func;
134     p_intf->p_sys->pf_callback[i_dummy + 1] = NULL;
135 }
136
137 /*****************************************************************************
138  * Functions exported as capabilities. They are declared as static so that
139  * we don't pollute the namespace too much.
140  *****************************************************************************/
141 static void intf_getfunctions( function_list_t * p_function_list )
142 {
143     p_function_list->functions.intf.pf_open  = intf_Open;
144     p_function_list->functions.intf.pf_close = intf_Close;
145     p_function_list->functions.intf.pf_run   = intf_Run;
146 }
147
148 /*****************************************************************************
149  * intf_Open: initialize and create window
150  *****************************************************************************/
151 static int intf_Open( intf_thread_t *p_intf )
152 {
153     /* Allocate instance and initialize some members */
154     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
155     if( p_intf->p_sys == NULL )
156     {
157         intf_ErrMsg("error: %s", strerror(ENOMEM));
158         return( 1 );
159     }
160
161     p_intf->p_sys->p_sub = intf_MsgSub();
162
163     /* Initialize Gnome thread */
164     p_intf->p_sys->b_playing = 0;
165     p_intf->p_sys->b_popup_changed = 0;
166     p_intf->p_sys->b_window_changed = 0;
167     p_intf->p_sys->b_playlist_changed = 0;
168
169     p_intf->p_sys->i_playing = -1;
170     p_intf->p_sys->b_slider_free = 1;
171
172     p_intf->p_sys->pf_callback[0] = NULL;
173
174     return( 0 );
175 }
176
177 /*****************************************************************************
178  * intf_Close: destroy interface window
179  *****************************************************************************/
180 static void intf_Close( intf_thread_t *p_intf )
181 {
182     intf_MsgUnsub( p_intf->p_sys->p_sub );
183
184     /* Destroy structure */
185     free( p_intf->p_sys );
186 }
187
188 /*****************************************************************************
189  * intf_Run: Gnome thread
190  *****************************************************************************
191  * this part of the interface is in a separate thread so that we can call
192  * gtk_main() from within it without annoying the rest of the program.
193  * XXX: the approach may look kludgy, and probably is, but I could not find
194  * a better way to dynamically load a Gnome interface at runtime.
195  *****************************************************************************/
196 static void intf_Run( intf_thread_t *p_intf )
197 {
198     /* gnome_init needs to know the command line. We don't care, so we
199      * give it an empty one */
200     char *p_args[] = { "" };
201     int   i_args   = 1;
202     int   i_dummy;
203
204     /* The data types we are allowed to receive */
205     static GtkTargetEntry target_table[] =
206     {
207         { "STRING", 0, DROP_ACCEPT_STRING },
208         { "text/uri-list", 0, DROP_ACCEPT_TEXT_URI_LIST },
209         { "text/plain",    0, DROP_ACCEPT_TEXT_PLAIN }
210     };
211
212     /* Initialize Gnome */
213     gnome_init( p_main->psz_arg0, VERSION, i_args, p_args );
214
215     /* Create some useful widgets that will certainly be used */
216     p_intf->p_sys->p_window = create_intf_window( );
217     p_intf->p_sys->p_popup = create_intf_popup( );
218     p_intf->p_sys->p_playlist = create_intf_playlist();
219     p_intf->p_sys->p_messages = create_intf_messages();
220
221     /* Set the title of the main window */
222     gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
223                           VOUT_TITLE " (Gnome interface)");
224
225     /* Accept file drops on the main window */
226     gtk_drag_dest_set( GTK_WIDGET( p_intf->p_sys->p_window ),
227                        GTK_DEST_DEFAULT_ALL, target_table,
228                        1, GDK_ACTION_COPY );
229     /* Accept file drops on the playlist window */
230     gtk_drag_dest_set( GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
231                             p_intf->p_sys->p_playlist ), "playlist_clist") ),
232                        GTK_DEST_DEFAULT_ALL, target_table,
233                        1, GDK_ACTION_COPY );
234
235     /* Get the slider object */
236     p_intf->p_sys->p_slider_frame = gtk_object_get_data(
237                       GTK_OBJECT( p_intf->p_sys->p_window ), "slider_frame" );
238
239     /* Configure the log window */
240     p_intf->p_sys->p_messages_text = GTK_TEXT( gtk_object_get_data(
241         GTK_OBJECT(p_intf->p_sys->p_messages ), "messages_textbox" ) );
242     gtk_text_set_line_wrap( p_intf->p_sys->p_messages_text, TRUE);
243     gtk_text_set_word_wrap( p_intf->p_sys->p_messages_text, FALSE);
244
245     /* Get the interface labels */
246     #define P_LABEL( name ) GTK_LABEL( gtk_object_get_data( \
247                          GTK_OBJECT( p_intf->p_sys->p_window ), name ) )
248     p_intf->p_sys->p_label_title = P_LABEL( "title_label" );
249     p_intf->p_sys->p_label_chapter = P_LABEL( "chapter_label" );
250     #undef P_LABEL
251
252     /* Connect the date display to the slider */
253     #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
254                          GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
255     p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );
256
257     gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
258                          GTK_SIGNAL_FUNC( GtkDisplayDate ), NULL );
259     p_intf->p_sys->f_adj_oldvalue = 0;
260     #undef P_SLIDER
261
262     /* We don't create these ones yet because we perhaps won't need them */
263     p_intf->p_sys->p_about = NULL;
264     p_intf->p_sys->p_modules = NULL;
265     p_intf->p_sys->p_fileopen = NULL;
266     p_intf->p_sys->p_disc = NULL;
267     p_intf->p_sys->p_network = NULL;
268     p_intf->p_sys->p_sat = NULL;
269     p_intf->p_sys->p_jump = NULL;
270     p_intf->p_sys->p_tooltips = gtk_tooltips_new();
271
272     /* Hide tooltips if the option is set */
273     if( config_GetIntVariable( "gnome-notooltips" ) )
274         gtk_tooltips_disable( p_intf->p_sys->p_tooltips );
275
276     /* Hide toolbar text of the option is set */
277     if ( config_GetIntVariable( "gnome-notoolbartext" ) )
278         gtk_toolbar_set_style(
279             GTK_TOOLBAR(lookup_widget( p_intf->p_sys->p_window, "toolbar" )),
280             GTK_TOOLBAR_ICONS );
281
282     /* Store p_intf to keep an eye on it */
283     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
284                          "p_intf", p_intf );
285
286     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_popup),
287                          "p_intf", p_intf );
288
289     gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_playlist ),
290                          "p_intf", p_intf );
291
292     gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_messages ),
293                          "p_intf", p_intf );
294
295     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
296                          "p_intf", p_intf );
297
298     /* Show the control window */
299     gtk_widget_show( p_intf->p_sys->p_window );
300
301     /* Sleep to avoid using all CPU - since some interfaces needs to access
302      * keyboard events, a 100ms delay is a good compromise */
303     i_dummy = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, GnomeManage, p_intf );
304
305     /* Enter gnome mode */
306     gtk_main();
307
308     /* Remove the timeout */
309     gtk_timeout_remove( i_dummy );
310
311     /* Destroy the Tooltips structure */
312     gtk_object_destroy( GTK_OBJECT(p_intf->p_sys->p_tooltips) );
313
314     /* Get rid of stored callbacks so we can unload the plugin */
315     for( i_dummy = 0;
316          i_dummy < MAX_ATEXIT && p_intf->p_sys->pf_callback[i_dummy] != NULL;
317          i_dummy++ )
318     {
319         p_intf->p_sys->pf_callback[i_dummy]();
320     }
321 }
322
323 /* following functions are local */
324
325 /*****************************************************************************
326  * GnomeManage: manage main thread messages
327  *****************************************************************************
328  * In this function, called approx. 10 times a second, we check what the
329  * main program wanted to tell us.
330  *****************************************************************************/
331 static gint GnomeManage( gpointer p_data )
332 {
333 #define p_intf ((intf_thread_t *)p_data)
334     static GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
335     static GdkColor red   = { 0, 0xffff, 0x6666, 0x6666 };
336     static GdkColor gray  = { 0, 0xaaaa, 0xaaaa, 0xaaaa };
337     GdkColor *p_color;
338
339     int i_start, i_stop;
340
341     vlc_mutex_lock( &p_intf->change_lock );
342
343     /* If the "display popup" flag has changed */
344     if( p_intf->b_menu_change )
345     {
346         if( !GTK_IS_WIDGET( p_intf->p_sys->p_popup ) )
347         {
348             p_intf->p_sys->p_popup = create_intf_popup();
349             gtk_object_set_data( GTK_OBJECT( p_intf->p_sys->p_popup ),
350                                  "p_popup", p_intf );
351         }
352
353         gnome_popup_menu_do_popup( p_intf->p_sys->p_popup,
354                                    NULL, NULL, NULL, NULL );
355         p_intf->b_menu_change = 0;
356     }
357
358     /* Update the log window */
359     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
360     i_stop = *p_intf->p_sys->p_sub->pi_stop;
361     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
362
363     if( p_intf->p_sys->p_sub->i_start != i_stop )
364     {
365         for( i_start = p_intf->p_sys->p_sub->i_start;
366              i_start != i_stop;
367              i_start = (i_start+1) % INTF_MSG_QSIZE )
368         {
369             /* Append all messages to log window */
370             switch( p_intf->p_sys->p_sub->p_msg[i_start].i_type )
371             {
372             case INTF_MSG_ERR:
373                 p_color = &red;
374                 break;
375             case INTF_MSG_WARN:
376                 p_color = &gray;
377                 break;
378             default:
379                 p_color = &white;
380                 break;
381             }
382
383             gtk_text_insert( p_intf->p_sys->p_messages_text, NULL, p_color,
384                 NULL, p_intf->p_sys->p_sub->p_msg[i_start].psz_msg, -1 );
385             gtk_text_insert( p_intf->p_sys->p_messages_text, NULL, p_color,
386                 NULL, "\n", -1 );
387         }
388
389         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
390         p_intf->p_sys->p_sub->i_start = i_start;
391         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
392
393         gtk_text_set_point( p_intf->p_sys->p_messages_text,
394                     gtk_text_get_length( p_intf->p_sys->p_messages_text ) );
395     }
396
397     /* Update the playlist */
398     GtkPlayListManage( p_intf );
399
400     if( p_input_bank->pp_input[0] != NULL && !p_intf->b_die )
401     {
402         vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
403
404         if( !p_input_bank->pp_input[0]->b_die )
405         {
406             /* New input or stream map change */
407             if( p_input_bank->pp_input[0]->stream.b_changed )
408             {
409                 GtkModeManage( p_intf );
410                 GtkSetupMenus( p_intf );
411                 p_intf->p_sys->b_playing = 1;
412             }
413
414             /* Manage the slider */
415             if( p_input_bank->pp_input[0]->stream.b_seekable &&
416                 p_intf->p_sys->b_playing )
417             {
418                 float           newvalue;
419                 newvalue = p_intf->p_sys->p_adj->value;
420
421 #define p_area p_input_bank->pp_input[0]->stream.p_selected_area
422                 /* If the user hasn't touched the slider since the last time,
423                  * then the input can safely change it */
424                 if( newvalue == p_intf->p_sys->f_adj_oldvalue )
425                 {
426                     /* Update the value */
427                     p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue =
428                         ( 100. * p_area->i_tell ) / p_area->i_size;
429
430                     gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
431                                              "value_changed" );
432                 }
433                 /* Otherwise, send message to the input if the user has
434                  * finished dragging the slider */
435                 else if( p_intf->p_sys->b_slider_free )
436                 {
437                     off_t i_seek = ( newvalue * p_area->i_size ) / 100;
438
439                     vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
440                     input_Seek( p_input_bank->pp_input[0], i_seek );
441                     vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
442
443                     /* Update the old value */
444                     p_intf->p_sys->f_adj_oldvalue = newvalue;
445                 }
446 #undef p_area
447             }
448
449             if( p_intf->p_sys->i_part !=
450                 p_input_bank->pp_input[0]->stream.p_selected_area->i_part )
451             {
452                 p_intf->p_sys->b_chapter_update = 1;
453                 GtkSetupMenus( p_intf );
454             }
455         }
456
457         vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
458     }
459     else if( p_intf->p_sys->b_playing && !p_intf->b_die )
460     {
461         GtkModeManage( p_intf );
462         p_intf->p_sys->b_playing = 0;
463     }
464
465     /* Manage core vlc functions through the callback */
466     p_intf->pf_manage( p_intf );
467
468     if( p_intf->b_die )
469     {
470         vlc_mutex_unlock( &p_intf->change_lock );
471
472         /* Prepare to die, young Skywalker */
473         gtk_main_quit();
474
475         /* Just in case */
476         return( FALSE );
477     }
478
479     vlc_mutex_unlock( &p_intf->change_lock );
480
481     return( TRUE );
482
483 #undef p_intf
484 }
485