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