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