]> git.sesse.net Git - vlc/blob - plugins/gnome/intf_gnome.c
A few new things:
[vlc] / plugins / gnome / intf_gnome.c
1 /*****************************************************************************
2  * intf_gnome.c: Gnome interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  *
6  * Authors:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include "defs.h"
27
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <stdlib.h>                                                /* free() */
30 #include <string.h>                                            /* strerror() */
31 #include <sys/types.h>                        /* on BSD, uio.h needs types.h */
32 #include <sys/uio.h>                                          /* for input.h */
33
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36 #include <X11/keysym.h>
37
38 #include "config.h"
39 #include "common.h"
40 #include "threads.h"
41 #include "mtime.h"
42 #include "plugins.h"
43
44 #include "input.h"
45 #include "video.h"
46 #include "video_output.h"
47
48 #include "audio_output.h" /* needed for mute */
49
50 #include "intf_msg.h"
51 #include "interface.h"
52
53 #include "main.h"
54
55 #include <stdio.h>
56
57 #include <gnome.h>
58
59 #include "intf_gnome_thread.h"
60 #include "intf_gnome.h"
61 #include "intf_gnome_interface.h"
62 #include "intf_gnome_support.h"
63
64 /*****************************************************************************
65  * intf_GnomeCreate: initialize and create window
66  *****************************************************************************/
67 int intf_GnomeCreate( intf_thread_t *p_intf )
68 {
69     char       *psz_display;
70
71     /* Allocate instance and initialize some members */
72     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
73     if( p_intf->p_sys == NULL )
74     {
75         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
76         return( 1 );
77     }
78
79     p_intf->p_sys->p_gnome = malloc( sizeof( gnome_thread_t ) );
80     if( p_intf->p_sys->p_gnome == NULL )
81     {
82         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
83         free( p_intf->p_sys );
84         return( 1 );
85     }
86
87     /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
88     psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
89     p_intf->p_sys->p_display = XOpenDisplay( psz_display );
90     if( !p_intf->p_sys->p_display )                                 /* error */
91     {
92         intf_ErrMsg("error: can't open display %s\n", psz_display );
93         free( p_intf->p_sys->p_gnome );
94         free( p_intf->p_sys );
95         return( 1 );
96     }
97     p_intf->p_sys->i_screen = DefaultScreen( p_intf->p_sys->p_display );
98
99     /* Spawn base window - this window will include the video output window */
100     if( GnomeCreateWindow( p_intf ) )
101     {
102         intf_ErrMsg( "error: can't create output window\n" );
103         XCloseDisplay( p_intf->p_sys->p_display );
104         free( p_intf->p_sys->p_gnome );
105         free( p_intf->p_sys );
106         return( 1 );
107     }
108
109     /* Spawn video output thread */
110     if( p_main->b_video )
111     {
112         p_intf->p_vout = vout_CreateThread( psz_display, p_intf->p_sys->window,
113                                             p_intf->p_sys->i_width,
114                                             p_intf->p_sys->i_height, NULL, 0,
115                                             (void *)&p_intf->p_sys->colormap );
116
117         if( p_intf->p_vout == NULL )                                /* error */
118         {
119             intf_ErrMsg("error: can't create video output thread\n" );
120             GnomeDestroyWindow( p_intf );
121             XCloseDisplay( p_intf->p_sys->p_display );
122             free( p_intf->p_sys->p_gnome );
123             free( p_intf->p_sys );
124             return( 1 );
125         }
126     }
127
128     /* Spawn Gnome thread */
129     p_intf->p_sys->p_gnome->b_die = 0;
130     p_intf->p_sys->p_gnome->b_error = 0;
131     
132     p_intf->p_sys->p_gnome->b_popup_changed = 0;
133     p_intf->p_sys->p_gnome->b_window_changed = 0;
134     p_intf->p_sys->p_gnome->b_playlist_changed = 0;
135
136     vlc_thread_create( &p_intf->p_sys->p_gnome->thread_id, "gnome",
137                        (void *)GnomeThread, p_intf->p_sys->p_gnome );
138
139     /* create basic key bindings */
140     intf_AssignNormalKeys( p_intf );
141     
142
143     /* Disable screen saver and return */
144     p_intf->p_sys->i_ss_count = 1;
145     GnomeDisableScreenSaver( p_intf );
146     return( 0 );
147 }
148
149 /*****************************************************************************
150  * intf_GnomeDestroy: destroy interface window
151  *****************************************************************************/
152 void intf_GnomeDestroy( intf_thread_t *p_intf )
153 {
154     /* Enable screen saver */
155     GnomeEnableScreenSaver( p_intf );
156
157     /* Close input thread, if any (blocking) */
158     if( p_intf->p_input )
159     {
160         input_DestroyThread( p_intf->p_input, NULL );
161     }
162
163     /* Close video output thread, if any (blocking) */
164     if( p_intf->p_vout )
165     {
166         vout_DestroyThread( p_intf->p_vout, NULL );
167     }
168
169     /* Close gnome thread, if any (blocking) */
170     if( p_intf->p_sys->p_gnome->thread_id )
171     {
172         p_intf->p_sys->p_gnome->b_die = 1;
173         intf_Msg( "waiting for Gnome thread to terminate\n" );
174         vlc_thread_join( p_intf->p_sys->p_gnome->thread_id );
175         intf_Msg( "Gnome thread terminated\n" );
176     }
177
178     /* Close main window and display */
179     GnomeDestroyWindow( p_intf );
180     XCloseDisplay( p_intf->p_sys->p_display );
181
182     /* Destroy structures */
183     free( p_intf->p_sys->p_gnome );
184     free( p_intf->p_sys );
185 }
186
187 /*****************************************************************************
188  * intf_GnomeManage: event loop
189  *****************************************************************************/
190 void intf_GnomeManage( intf_thread_t *p_intf )
191 {
192     /* Manage main window */
193     GnomeManageWindow( p_intf );
194
195     /* Manage messages from the Gnome interface */
196     GnomeManageInterface( p_intf );
197 }
198
199 /* following functions are local */
200
201 /*****************************************************************************
202  * GnomeCreateWindow: open and set-up X11 main window
203  *****************************************************************************/
204 static int GnomeCreateWindow( intf_thread_t *p_intf )
205 {
206     XSizeHints              xsize_hints;
207     XSetWindowAttributes    xwindow_attributes;
208     XGCValues               xgcvalues;
209     XEvent                  xevent;
210     boolean_t               b_expose;
211     boolean_t               b_configure_notify;
212     boolean_t               b_map_notify;
213
214     /* Set main window's size */
215     p_intf->p_sys->i_width =  main_GetIntVariable( VOUT_WIDTH_VAR,
216                                                    VOUT_WIDTH_DEFAULT );
217     p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
218                                                    VOUT_HEIGHT_DEFAULT );
219
220     /* Prepare window manager hints and properties */
221     xsize_hints.base_width =            p_intf->p_sys->i_width;
222     xsize_hints.base_height =           p_intf->p_sys->i_height;
223     xsize_hints.flags =                 PSize;
224     p_intf->p_sys->wm_protocols =       XInternAtom( p_intf->p_sys->p_display,
225                                                      "WM_PROTOCOLS", True );
226     p_intf->p_sys->wm_delete_window =   XInternAtom( p_intf->p_sys->p_display,
227                                                      "WM_DELETE_WINDOW", True );
228
229     /* Prepare window attributes */
230     xwindow_attributes.backing_store = Always;       /* save the hidden part */
231     xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
232                                                       p_intf->p_sys->i_screen );
233
234     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
235
236     /* Create the window and set hints - the window must receive ConfigureNotify
237      * events, and, until it is displayed, Expose and MapNotify events. */
238     p_intf->p_sys->window =
239             XCreateWindow( p_intf->p_sys->p_display,
240                            DefaultRootWindow( p_intf->p_sys->p_display ),
241                            0, 0,
242                            p_intf->p_sys->i_width, p_intf->p_sys->i_height, 1,
243                            0, InputOutput, 0,
244                            CWBackingStore | CWBackPixel | CWEventMask,
245                            &xwindow_attributes );
246
247     /* Set window manager hints and properties: size hints, command,
248      * window's name, and accepted protocols */
249     XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window,
250                        &xsize_hints );
251     XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
252                  p_main->ppsz_argv, p_main->i_argc );
253     XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
254     if( (p_intf->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
255         || (p_intf->p_sys->wm_delete_window == None)
256         || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
257                              &p_intf->p_sys->wm_delete_window, 1 ) )
258     {
259         /* WM_DELETE_WINDOW is not supported by window manager */
260         intf_Msg("error: missing or bad window manager - please exit program kindly.\n");
261     }
262
263     /* Creation of a graphic context that doesn't generate a GraphicsExpose
264      * event when using functions like XCopyArea */
265     xgcvalues.graphics_exposures = False;
266     p_intf->p_sys->gc =  XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
267                                     GCGraphicsExposures, &xgcvalues);
268
269     /* Send orders to server, and wait until window is displayed - three
270      * events must be received: a MapNotify event, an Expose event allowing
271      * drawing in the window, and a ConfigureNotify to get the window
272      * dimensions. Once those events have been received, only ConfigureNotify
273      * events need to be received. */
274     b_expose = 0;
275     b_configure_notify = 0;
276     b_map_notify = 0;
277     XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
278     do
279     {
280         XNextEvent( p_intf->p_sys->p_display, &xevent);
281         if( (xevent.type == Expose)
282             && (xevent.xexpose.window == p_intf->p_sys->window) )
283         {
284             b_expose = 1;
285         }
286         else if( (xevent.type == MapNotify)
287                  && (xevent.xmap.window == p_intf->p_sys->window) )
288         {
289             b_map_notify = 1;
290         }
291         else if( (xevent.type == ConfigureNotify)
292                  && (xevent.xconfigure.window == p_intf->p_sys->window) )
293         {
294             b_configure_notify = 1;
295             p_intf->p_sys->i_width = xevent.xconfigure.width;
296             p_intf->p_sys->i_height = xevent.xconfigure.height;
297         }
298     } while( !( b_expose && b_configure_notify && b_map_notify ) );
299
300     XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
301                   StructureNotifyMask | KeyPressMask | ButtonPressMask );
302
303     if( XDefaultDepth(p_intf->p_sys->p_display, p_intf->p_sys->i_screen) == 8 )
304     {
305         /* Allocate a new palette */
306         p_intf->p_sys->colormap = XCreateColormap( p_intf->p_sys->p_display,
307                               DefaultRootWindow( p_intf->p_sys->p_display ),
308                               DefaultVisual( p_intf->p_sys->p_display,
309                                              p_intf->p_sys->i_screen ),
310                               AllocAll );
311
312         xwindow_attributes.colormap = p_intf->p_sys->colormap;
313         XChangeWindowAttributes( p_intf->p_sys->p_display,
314                                  p_intf->p_sys->window,
315                                  CWColormap, &xwindow_attributes );
316     }
317
318     /* At this stage, the window is open, displayed, and ready to receive data */
319     return( 0 );
320 }
321
322 /*****************************************************************************
323  * GnomeDestroyWindow: destroy X11 main window
324  *****************************************************************************/
325 static void GnomeDestroyWindow( intf_thread_t *p_intf )
326 {
327     XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
328     XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
329     XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
330 }
331
332 /*****************************************************************************
333  * GnomeManageWindow: manage X11 main window
334  *****************************************************************************/
335 static void GnomeManageWindow( intf_thread_t *p_intf )
336 {
337     XEvent      xevent;                                         /* X11 event */
338     boolean_t   b_resized;                        /* window has been resized */
339     char        i_key;                                    /* ISO Latin-1 key */
340
341     /* Handle X11 events: ConfigureNotify events are parsed to know if the
342      * output window's size changed, MapNotify and UnmapNotify to know if the
343      * window is mapped (and if the display is useful), and ClientMessages
344      * to intercept window destruction requests */
345     b_resized = 0;
346     while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
347                               StructureNotifyMask | KeyPressMask |
348                               ButtonPressMask, &xevent ) == True )
349     {
350         /* ConfigureNotify event: prepare  */
351         if( (xevent.type == ConfigureNotify)
352             && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
353                 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
354         {
355             /* Update dimensions */
356             b_resized = 1;
357             p_intf->p_sys->i_width = xevent.xconfigure.width;
358             p_intf->p_sys->i_height = xevent.xconfigure.height;
359         }
360         /* MapNotify event: change window status and disable screen saver */
361         else if( xevent.type == MapNotify)
362         {
363             if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
364             {
365                 GnomeDisableScreenSaver( p_intf );
366                 p_intf->p_vout->b_active = 1;
367             }
368         }
369         /* UnmapNotify event: change window status and enable screen saver */
370         else if( xevent.type == UnmapNotify )
371         {
372             if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
373             {
374                 GnomeEnableScreenSaver( p_intf );
375                 p_intf->p_vout->b_active = 0;
376             }
377         }
378         /* Keyboard event */
379         else if( xevent.type == KeyPress )
380         {
381             if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
382             {
383                 if( intf_ProcessKey( p_intf, i_key ) )
384                 {
385                     intf_DbgMsg( "unhandled key '%c' (%i)\n", (char) i_key, i_key );
386                 }
387             }
388         }
389         /* Mouse click */
390         else if( xevent.type == ButtonPress )
391         {
392             switch( ((XButtonEvent *)&xevent)->button )
393             {
394                 case Button1:
395                     /* in this part we will eventually manage
396                      * clicks for DVD navigation for instance */
397                     break;
398
399                 case Button2:
400                     GnomeTogglePointer( p_intf );
401                     break;
402
403                 case Button3:
404                     /* toggle the menu display */
405                     vlc_mutex_lock( &p_intf->p_sys->p_gnome->change_lock );
406                     p_intf->p_sys->p_gnome->b_popup_changed = 1;
407                     vlc_mutex_unlock( &p_intf->p_sys->p_gnome->change_lock );
408                     break;
409             }
410
411         }
412 #ifdef DEBUG
413         /* Other event */
414         else
415         {
416             intf_DbgMsg( "%p -> unhandled event type %d received\n",
417                          p_intf, xevent.type );
418         }
419 #endif
420     }
421
422     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
423      * are handled - according to the man pages, the format is always 32
424      * in this case */
425     while( XCheckTypedEvent( p_intf->p_sys->p_display,
426                              ClientMessage, &xevent ) )
427     {
428         if( (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
429             && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
430         {
431             p_intf->b_die = 1;
432         }
433         else
434         {
435             intf_DbgMsg( "%p -> unhandled ClientMessage received\n", p_intf );
436         }
437     }
438
439     /*
440      * Handle vout or interface windows resizing
441      */
442     if( p_intf->p_vout != NULL )
443     {
444         if( b_resized )
445         {
446             /* If interface window has been resized, change vout size */
447             intf_DbgMsg( "resizing output window\n" );
448             vlc_mutex_lock( &p_intf->p_vout->change_lock );
449             p_intf->p_vout->i_width =  p_intf->p_sys->i_width;
450             p_intf->p_vout->i_height = p_intf->p_sys->i_height;
451             p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
452             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
453         }
454         else if( (p_intf->p_vout->i_width  != p_intf->p_sys->i_width) ||
455                  (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
456         {
457            /* If video output size has changed, change interface window size */
458             intf_DbgMsg( "resizing output window\n" );
459             p_intf->p_sys->i_width =    p_intf->p_vout->i_width;
460             p_intf->p_sys->i_height =   p_intf->p_vout->i_height;
461             XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
462                            p_intf->p_sys->i_width, p_intf->p_sys->i_height );
463         }
464     }
465 }
466
467 /*****************************************************************************
468  * GnomeEnableScreenSaver: enable screen saver
469  *****************************************************************************
470  * This function enable the screen saver on a display after it had been
471  * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
472  * know wether the screen saver can be activated or not: if n successive calls
473  * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
474  * will be required before the screen saver could effectively be activated.
475  *****************************************************************************/
476 void GnomeEnableScreenSaver( intf_thread_t *p_intf )
477 {
478     if( p_intf->p_sys->i_ss_count++ == 0 )
479     {
480         intf_Msg( "Enabling screen saver\n" );
481         XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
482                          p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
483                          p_intf->p_sys->i_ss_exposure );
484     }
485 }
486
487 /*****************************************************************************
488  * GnomeDisableScreenSaver: disable screen saver
489  *****************************************************************************
490  * See XEnableScreenSaver
491  *****************************************************************************/
492 void GnomeDisableScreenSaver( intf_thread_t *p_intf )
493 {
494     if( --p_intf->p_sys->i_ss_count == 0 )
495     {
496         /* Save screen saver informations */
497         XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
498                          &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
499                          &p_intf->p_sys->i_ss_exposure );
500
501         /* Disable screen saver */
502         intf_Msg("Disabling screen saver\n");
503         XSetScreenSaver( p_intf->p_sys->p_display, 0,
504                          p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
505                          p_intf->p_sys->i_ss_exposure );
506     }
507 }
508
509 /*****************************************************************************
510  * GnomeTogglePointer: hide or show the mouse pointer
511  *****************************************************************************
512  * This function hides the X pointer if it is visible by putting it at
513  * coordinates (32,32) and setting the pointer sprite to a blank one. To
514  * show it again, we disable the sprite and restore the original coordinates.
515  *****************************************************************************/
516 void GnomeTogglePointer( intf_thread_t *p_intf )
517 {
518     static Cursor cursor;
519     static boolean_t b_cursor = 0;
520
521     if( p_intf->p_sys->b_mouse )
522     {
523         p_intf->p_sys->b_mouse = 0;
524
525         if( !b_cursor )
526         {
527             XColor color;
528             Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
529                                DefaultRootWindow(p_intf->p_sys->p_display),
530                                1, 1, 1 );
531
532             XParseColor( p_intf->p_sys->p_display,
533                          XCreateColormap( p_intf->p_sys->p_display,
534                                           DefaultRootWindow(
535                                                   p_intf->p_sys->p_display ),
536                                           DefaultVisual(
537                                                   p_intf->p_sys->p_display,
538                                                   p_intf->p_sys->i_screen ),
539                                           AllocNone ),
540                          "black", &color );
541
542             cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
543                            blank, blank, &color, &color, 1, 1 );
544
545             b_cursor = 1;
546         }
547         XDefineCursor( p_intf->p_sys->p_display,
548                        p_intf->p_sys->window, cursor );
549     }
550     else
551     {
552         p_intf->p_sys->b_mouse = 1;
553
554         XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );
555     }
556 }
557
558 /*****************************************************************************
559  * GnomeManageInterface: manage messages from the Gnome interface
560
561  *****************************************************************************
562  * In this function, called approx. 10 times a second, we check what the
563  * Gnome interface wanted to tell us.
564  *****************************************************************************/
565 static void GnomeManageInterface( intf_thread_t *p_intf )
566 {
567     gnome_thread_t *p_gnome = p_intf->p_sys->p_gnome;
568
569     /* lock the change structure */
570     vlc_mutex_lock( &p_gnome->change_lock );
571
572     /* you killed my father, prepare to die */
573     if( p_gnome->b_die )
574     {
575         p_intf->b_die = 1;
576     }
577
578     if( p_gnome->b_activity_changed )
579     {
580         vlc_mutex_lock( &p_intf->p_vout->picture_lock );
581         p_intf->p_vout->b_active = p_gnome->b_activity;
582         /* having to access p_main sucks */
583         p_main->p_aout->b_active = p_gnome->b_activity;
584         vlc_mutex_unlock( &p_intf->p_vout->picture_lock );
585
586         p_gnome->b_activity_changed = 0;
587     }
588
589     /* unlock the change structure */
590     vlc_mutex_unlock( &p_gnome->change_lock );
591 }
592
593 /*****************************************************************************
594  * GnomeManageMain: manage main thread messages
595  *****************************************************************************
596  * In this function, called approx. 10 times a second, we check what the
597  * main program wanted to tell us.
598  *****************************************************************************/
599 static gint GnomeManageMain( gpointer p_data )
600 {
601     gnome_thread_t *p_gnome = (void *)p_data;
602
603     /* lock the change structure */
604     vlc_mutex_lock( &p_gnome->change_lock );
605
606     if( p_gnome->b_die )
607     {
608         /* unlock the change structure */
609         vlc_mutex_unlock( &p_gnome->change_lock );
610
611         /* prepare to die, young man */
612         gtk_main_quit();
613         return( FALSE );
614     }
615
616     /* if the "display popup" flag has changed */
617     if( p_gnome->b_popup_changed )
618     {
619         gnome_popup_menu_do_popup( p_gnome->p_popup,
620                                    NULL, NULL, NULL, NULL );
621         p_gnome->b_popup_changed = 0;
622     }
623
624     /* unlock the change structure */
625     vlc_mutex_unlock( &p_gnome->change_lock );
626
627     return( TRUE );
628 }
629
630 /*****************************************************************************
631  * GnomeThread: special Gnome thread
632  *****************************************************************************
633  * this part of the interface is in a separate thread so that we can call
634  * gtk_main() from within it without annoying the rest of the program.
635  * XXX: the approach may look kludgy, and probably is, but I could not find
636  * a better way to dynamically load a Gnome interface at runtime.
637  *****************************************************************************/
638 void GnomeThread( gnome_thread_t *p_gnome )
639 {
640     /* gnome_init needs to know the command line. We don't care, so we
641      * give it an empty one */
642     char *p_args[] = { };
643
644     /* Sleep to avoid using all CPU - since some interfaces needs to access
645      * keyboard events, a 100ms delay is a good compromise */
646     gtk_timeout_add( INTF_IDLE_SLEEP / 1000, GnomeManageMain, p_gnome );
647  
648     gnome_init( "vlc", VERSION, 1, p_args );
649
650     /* create some useful widgets that will certainly be used */
651     p_gnome->p_window = create_intf_window();
652     p_gnome->p_popup = create_intf_popup( );
653
654     /* we don't create these ones yet because we perhaps won't need them */
655     p_gnome->p_about = NULL;
656     p_gnome->p_playlist = NULL;
657
658     /* store p_sys to keep an eye on it */
659     gtk_object_set_data( GTK_OBJECT(p_gnome->p_window), "p_gnome", p_gnome );
660     gtk_object_set_data( GTK_OBJECT(p_gnome->p_popup), "p_gnome", p_gnome );
661
662     /* show the control window */
663     //gtk_widget_show( p_gnome->p_window );
664
665     /* enter gnome mode */
666     gtk_main();
667 }
668