]> git.sesse.net Git - vlc/blob - plugins/mga/intf_mga.c
A few new things:
[vlc] / plugins / mga / intf_mga.c
1 /*****************************************************************************
2  * intf_mga.c: MGA 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 "intf_msg.h"
49 #include "interface.h"
50
51 #include "main.h"
52
53 /*****************************************************************************
54  * intf_sys_t: description and status of X11 interface
55  *****************************************************************************/
56 typedef struct intf_sys_s
57 {
58     /* X11 generic properties */
59     Display *           p_display;                    /* X11 display pointer */
60     int                 i_screen;                              /* X11 screen */
61     Atom                wm_protocols;
62     Atom                wm_delete_window;
63
64     /* Main window properties */
65     Window              window;                               /* main window */
66     GC                  gc;               /* graphic context for main window */
67     int                 i_width;                     /* width of main window */
68     int                 i_height;                   /* height of main window */
69
70     /* Screen saver properties */
71     int                 i_ss_count;              /* enabling/disabling count */
72     int                 i_ss_timeout;                             /* timeout */
73     int                 i_ss_interval;           /* interval between changes */
74     int                 i_ss_blanking;                      /* blanking mode */
75     int                 i_ss_exposure;                      /* exposure mode */
76
77     /* Mouse pointer properties */
78     boolean_t           b_mouse;         /* is the mouse pointer displayed ? */
79
80 } intf_sys_t;
81
82 /*****************************************************************************
83  * Local prototypes
84  *****************************************************************************/
85 static int  X11CreateWindow             ( intf_thread_t *p_intf );
86 static void X11DestroyWindow            ( intf_thread_t *p_intf );
87 static void X11ManageWindow             ( intf_thread_t *p_intf );
88 static void X11EnableScreenSaver        ( intf_thread_t *p_intf );
89 static void X11DisableScreenSaver       ( intf_thread_t *p_intf );
90 static void X11TogglePointer            ( intf_thread_t *p_intf );
91
92 /*****************************************************************************
93  * intf_MGACreate: initialize and create window
94  *****************************************************************************/
95 int intf_MGACreate( intf_thread_t *p_intf )
96 {
97     char       *psz_display;
98
99     /* Allocate instance and initialize some members */
100     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
101     if( p_intf->p_sys == NULL )
102     {
103         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
104         return( 1 );
105     }
106
107     /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
108     psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
109     p_intf->p_sys->p_display = XOpenDisplay( psz_display );
110     if( !p_intf->p_sys->p_display )                                 /* error */
111     {
112         intf_ErrMsg("error: can't open display %s\n", psz_display );
113         free( p_intf->p_sys );
114         return( 1 );
115     }
116     p_intf->p_sys->i_screen = DefaultScreen( p_intf->p_sys->p_display );
117
118     /* Spawn base window - this window will include the video output window,
119      * but also command buttons, subtitles and other indicators */
120     if( X11CreateWindow( p_intf ) )
121     {
122         intf_ErrMsg("error: can't create interface window\n" );
123         XCloseDisplay( p_intf->p_sys->p_display );
124         free( p_intf->p_sys );
125         return( 1 );
126     }
127
128     /* Spawn video output thread */
129     if( p_main->b_video )
130     {
131         p_intf->p_vout = vout_CreateThread( psz_display, p_intf->p_sys->window,
132                                             p_intf->p_sys->i_width,
133                                             p_intf->p_sys->i_height, NULL, 0, NULL );
134         if( p_intf->p_vout == NULL )                                /* error */
135         {
136             intf_ErrMsg("error: can't create video output thread\n" );
137             X11DestroyWindow( p_intf );
138             XCloseDisplay( p_intf->p_sys->p_display );
139             free( p_intf->p_sys );
140             return( 1 );
141         }
142     }
143
144     p_intf->p_sys->b_mouse = 1;
145     
146     /* bind keys */
147     intf_AssignNormalKeys( p_intf );
148
149     
150     /* Disable screen saver and return */
151     p_intf->p_sys->i_ss_count = 1;
152     X11DisableScreenSaver( p_intf );
153     return( 0 );
154 }
155
156 /*****************************************************************************
157  * intf_MGADestroy: destroy interface window
158  *****************************************************************************/
159 void intf_MGADestroy( intf_thread_t *p_intf )
160 {
161     /* Enable screen saver */
162     X11EnableScreenSaver( p_intf );
163
164     /* Close input thread, if any (blocking) */
165     if( p_intf->p_input )
166     {
167         input_DestroyThread( p_intf->p_input, NULL );
168     }
169
170     /* Close video output thread, if any (blocking) */
171     if( p_intf->p_vout )
172     {
173         vout_DestroyThread( p_intf->p_vout, NULL );
174     }
175
176     /* Close main window and display */
177     X11DestroyWindow( p_intf );
178     XCloseDisplay( p_intf->p_sys->p_display );
179
180     /* Destroy structure */
181     free( p_intf->p_sys );
182 }
183
184
185 /*****************************************************************************
186  * intf_MGAManage: event loop
187  *****************************************************************************/
188 void intf_MGAManage( intf_thread_t *p_intf )
189 {
190     /* Manage main window */
191     X11ManageWindow( p_intf );
192 }
193
194 /* following functions are local */
195
196 /*****************************************************************************
197  * X11CreateWindow: open and set-up X11 main window
198  *****************************************************************************/
199 static int X11CreateWindow( intf_thread_t *p_intf )
200 {
201     XSizeHints              xsize_hints;
202     XSetWindowAttributes    xwindow_attributes;
203     XGCValues               xgcvalues;
204     XEvent                  xevent;
205     boolean_t               b_expose;
206     boolean_t               b_configure_notify;
207     boolean_t               b_map_notify;
208
209     /* Set main window's size */
210     p_intf->p_sys->i_width =  main_GetIntVariable( VOUT_WIDTH_VAR, VOUT_WIDTH_DEFAULT );
211     p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR, VOUT_HEIGHT_DEFAULT );
212
213     /* Prepare window manager hints and properties */
214     xsize_hints.base_width =            p_intf->p_sys->i_width;
215     xsize_hints.base_height =           p_intf->p_sys->i_height;
216     xsize_hints.flags =                 PSize;
217     p_intf->p_sys->wm_protocols =       XInternAtom( p_intf->p_sys->p_display,
218                                                      "WM_PROTOCOLS", True );
219     p_intf->p_sys->wm_delete_window =   XInternAtom( p_intf->p_sys->p_display,
220                                                      "WM_DELETE_WINDOW", True );
221
222     /* Prepare window attributes */
223     xwindow_attributes.backing_store = Always;       /* save the hidden part */
224     xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
225                                                       p_intf->p_sys->i_screen );
226
227     /* Create the window and set hints - the window must receive ConfigureNotify
228      * events, and, until it is displayed, Expose and MapNotify events. */
229     p_intf->p_sys->window = XCreateSimpleWindow( p_intf->p_sys->p_display,
230                                          DefaultRootWindow( p_intf->p_sys->p_display ),
231                                          0, 0,
232                                          p_intf->p_sys->i_width, p_intf->p_sys->i_height,
233                                          0, 0, 0);
234     XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
235                   ExposureMask | StructureNotifyMask );
236     XChangeWindowAttributes( p_intf->p_sys->p_display, p_intf->p_sys->window,
237                              CWBackingStore | CWBackPixel, &xwindow_attributes);
238
239     /* Set window manager hints and properties: size hints, command, window's name,
240      * and accepted protocols */
241     XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window, &xsize_hints );
242     XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
243                  p_main->ppsz_argv, p_main->i_argc );
244     XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
245     if( (p_intf->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
246         || (p_intf->p_sys->wm_delete_window == None)
247         || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
248                              &p_intf->p_sys->wm_delete_window, 1 ) )
249     {
250         /* WM_DELETE_WINDOW is not supported by window manager */
251         intf_Msg("error: missing or bad window manager - please exit program kindly.\n");
252     }
253
254     /* Creation of a graphic context that doesn't generate a GraphicsExpose event
255        when using functions like XCopyArea */
256     xgcvalues.graphics_exposures = False;
257     p_intf->p_sys->gc =  XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
258                                     GCGraphicsExposures, &xgcvalues);
259
260     /* Send orders to server, and wait until window is displayed - three events
261      * must be received: a MapNotify event, an Expose event allowing drawing in the
262      * window, and a ConfigureNotify to get the window dimensions. Once those events
263      * have been received, only ConfigureNotify events need to be received. */
264     b_expose = 0;
265     b_configure_notify = 0;
266     b_map_notify = 0;
267     XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
268     do
269     {
270         XNextEvent( p_intf->p_sys->p_display, &xevent);
271         if( (xevent.type == Expose)
272             && (xevent.xexpose.window == p_intf->p_sys->window) )
273         {
274             b_expose = 1;
275         }
276         else if( (xevent.type == MapNotify)
277                  && (xevent.xmap.window == p_intf->p_sys->window) )
278         {
279             b_map_notify = 1;
280         }
281         else if( (xevent.type == ConfigureNotify)
282                  && (xevent.xconfigure.window == p_intf->p_sys->window) )
283         {
284             b_configure_notify = 1;
285             p_intf->p_sys->i_width = xevent.xconfigure.width;
286             p_intf->p_sys->i_height = xevent.xconfigure.height;
287         }
288     }
289     while( !( b_expose && b_configure_notify && b_map_notify ) );
290     XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
291                   StructureNotifyMask | KeyPressMask | ButtonPressMask );
292
293     /* At this stage, the window is openned, displayed, and ready to receive data */
294     return( 0 );
295 }
296
297 /*****************************************************************************
298  * X11DestroyWindow: destroy X11 main window
299  *****************************************************************************/
300 static void X11DestroyWindow( intf_thread_t *p_intf )
301 {
302     XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
303     XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
304     XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
305 }
306
307 /*****************************************************************************
308  * X11ManageWindow: manage X11 main window
309  *****************************************************************************/
310 static void X11ManageWindow( intf_thread_t *p_intf )
311 {
312     XEvent      xevent;                                         /* X11 event */
313     boolean_t   b_resized;                        /* window has been resized */
314     char        i_key;                                    /* ISO Latin-1 key */
315
316     /* Handle X11 events: ConfigureNotify events are parsed to know if the
317      * output window's size changed, MapNotify and UnmapNotify to know if the
318      * window is mapped (and if the display is useful), and ClientMessages
319      * to intercept window destruction requests */
320     b_resized = 0;
321     while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
322                               StructureNotifyMask | KeyPressMask |
323                               ButtonPressMask, &xevent ) == True )
324     {
325         /* ConfigureNotify event: prepare  */
326         if( (xevent.type == ConfigureNotify)
327             && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
328                 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
329         {
330             /* Update dimensions */
331             b_resized = 1;
332             p_intf->p_sys->i_width = xevent.xconfigure.width;
333             p_intf->p_sys->i_height = xevent.xconfigure.height;
334         }
335         /* MapNotify event: change window status and disable screen saver */
336         else if( xevent.type == MapNotify)
337         {
338             if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
339             {
340                 X11DisableScreenSaver( p_intf );
341                 p_intf->p_vout->b_active = 1;
342             }
343         }
344         /* UnmapNotify event: change window status and enable screen saver */
345         else if( xevent.type == UnmapNotify )
346         {
347             if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
348             {
349                 X11EnableScreenSaver( p_intf );
350                 p_intf->p_vout->b_active = 0;
351             }
352         }
353         /* DestroyNotify event: window has been destroyed */
354         else if( xevent.type == DestroyNotify )
355         {
356             intf_ErrMsg( "vout: window destroyed !\n");
357         }
358         /* Keyboard event */
359         else if( xevent.type == KeyPress )
360         {
361             if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
362             {
363                 if( intf_ProcessKey( p_intf, i_key ) )
364                 {
365                     intf_DbgMsg("unhandled key '%c' (%i)\n", (char) i_key, i_key );
366                 }
367             }
368         }
369         /* Mouse click */
370         else if( xevent.type == ButtonPress )
371         {
372             switch( ((XButtonEvent *)&xevent)->button )
373             {
374                 case Button1:
375                     /* in this part we will eventually manage
376                      * clicks for DVD navigation for instance */
377                     break;
378
379                 case Button2:
380                     X11TogglePointer( p_intf );
381                     break;
382
383                 case Button3:
384                     vlc_mutex_lock( &p_intf->p_vout->change_lock );
385                     p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
386                     p_intf->p_vout->i_changes  |= VOUT_INTF_CHANGE;
387                     vlc_mutex_unlock( &p_intf->p_vout->change_lock );
388                     break;
389             }
390         }
391         /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
392          * are handled - according to the man pages, the format is always 32
393          * in this case */
394         else if( (xevent.type == ClientMessage)
395                  && (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
396                  && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
397         {
398             /* FIXME: this never happens :( how to receive wm messages ?? */
399             intf_DbgMsg("ClientMessage received\n");
400         }
401 #ifdef DEBUG
402         /* Other event */
403         else
404         {
405             intf_DbgMsg("%p -> unhandled event type %d received\n", p_intf, xevent.type );
406         }
407 #endif
408     }
409
410     /*
411      * Handle vout or interface windows resizing
412      */
413     if( p_intf->p_vout != NULL )
414     {
415         if( b_resized )
416         {
417             /* If interface window has been resized, change vout size */
418             intf_DbgMsg("resizing output window\n");
419             vlc_mutex_lock( &p_intf->p_vout->change_lock );
420             p_intf->p_vout->i_width =  p_intf->p_sys->i_width;
421             p_intf->p_vout->i_height = p_intf->p_sys->i_height;
422             p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
423             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
424         }
425         else if( (p_intf->p_vout->i_width  != p_intf->p_sys->i_width) ||
426                  (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
427         {
428            /* If video output size has changed, change interface window size */
429             intf_DbgMsg("resizing interface window\n");
430             p_intf->p_sys->i_width =    p_intf->p_vout->i_width;
431             p_intf->p_sys->i_height =   p_intf->p_vout->i_height;
432             XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
433                            p_intf->p_sys->i_width, p_intf->p_sys->i_height );
434         }
435     }
436 }
437
438 /*****************************************************************************
439  * X11EnableScreenSaver: enable screen saver
440  *****************************************************************************
441  * This function enable the screen saver on a display after it had been
442  * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
443  * know wether the screen saver can be activated or not: if n successive calls
444  * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
445  * will be required before the screen saver could effectively be activated.
446  *****************************************************************************/
447 void X11EnableScreenSaver( intf_thread_t *p_intf )
448 {
449     if( p_intf->p_sys->i_ss_count++ == 0 )
450     {
451         intf_Msg("Enabling screen saver\n");
452         XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
453                          p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
454                          p_intf->p_sys->i_ss_exposure );
455     }
456 }
457
458 /*****************************************************************************
459  * X11DisableScreenSaver: disable screen saver
460  *****************************************************************************
461  * See XEnableScreenSaver
462  *****************************************************************************/
463 void X11DisableScreenSaver( intf_thread_t *p_intf )
464 {
465     if( --p_intf->p_sys->i_ss_count == 0 )
466     {
467         /* Save screen saver informations */
468         XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
469                          &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
470                          &p_intf->p_sys->i_ss_exposure );
471
472         /* Disable screen saver */
473         intf_Msg("Disabling screen saver\n");
474         XSetScreenSaver( p_intf->p_sys->p_display, 0,
475                          p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
476                          p_intf->p_sys->i_ss_exposure );
477     }
478 }
479
480 /*****************************************************************************
481  * X11TogglePointer: hide or show the mouse pointer
482  *****************************************************************************
483  * This function hides the X pointer if it is visible by putting it at
484  * coordinates (32,32) and setting the pointer sprite to a blank one. To
485  * show it again, we disable the sprite and restore the original coordinates.
486  *****************************************************************************/
487 void X11TogglePointer( intf_thread_t *p_intf )
488 {
489     static Cursor cursor;
490     static boolean_t b_cursor = 0;
491
492     if( p_intf->p_sys->b_mouse )
493     {
494         p_intf->p_sys->b_mouse = 0;
495
496         if( !b_cursor )
497         {
498             XColor color;
499             Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
500                                DefaultRootWindow(p_intf->p_sys->p_display),
501                                1, 1, 1 );
502
503             XParseColor( p_intf->p_sys->p_display,
504                          XCreateColormap( p_intf->p_sys->p_display,
505                                           DefaultRootWindow(
506                                                   p_intf->p_sys->p_display ),
507                                           DefaultVisual(
508                                                   p_intf->p_sys->p_display,
509                                                   p_intf->p_sys->i_screen ),
510                                           AllocNone ),
511                          "black", &color );
512
513             cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
514                            blank, blank, &color, &color, 1, 1 );
515
516             b_cursor = 1;
517         }
518         XDefineCursor( p_intf->p_sys->p_display,
519                        p_intf->p_sys->window, cursor );
520     }
521     else
522     {
523         p_intf->p_sys->b_mouse = 1;
524
525         XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );
526     }
527 }