]> git.sesse.net Git - vlc/blob - plugins/x11/intf_x11.c
Another mistake.
[vlc] / plugins / x11 / intf_x11.c
1 /*****************************************************************************
2  * intf_x11.c: X11 interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  * $Id: intf_x11.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     Colormap            colormap;               /* colormap used (8bpp only) */
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_X11Create: initialize and create window
96  *****************************************************************************/
97 int intf_X11Create( 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", 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", 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" );
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,
136                                             (void *)&p_intf->p_sys->colormap );
137
138         if( p_intf->p_vout == NULL )                                /* error */
139         {
140             intf_ErrMsg("error: can't create video output thread" );
141             X11DestroyWindow( p_intf );
142             XCloseDisplay( p_intf->p_sys->p_display );
143             free( p_intf->p_sys );
144             return( 1 );
145         }
146     }
147
148     p_intf->p_sys->b_mouse = 1;
149
150     /* bind keys */
151     intf_AssignNormalKeys( p_intf );
152     
153     /* Disable screen saver and return */
154     p_intf->p_sys->i_ss_count = 1;
155     X11DisableScreenSaver( p_intf );
156     return( 0 );
157 }
158
159 /*****************************************************************************
160  * intf_X11Destroy: destroy interface window
161  *****************************************************************************/
162 void intf_X11Destroy( intf_thread_t *p_intf )
163 {
164     /* Enable screen saver */
165     X11EnableScreenSaver( p_intf );
166
167     /* Close input thread, if any (blocking) */
168     if( p_intf->p_input )
169     {
170         input_DestroyThread( p_intf->p_input, NULL );
171     }
172
173     /* Close video output thread, if any (blocking) */
174     if( p_intf->p_vout )
175     {
176         vout_DestroyThread( p_intf->p_vout, NULL );
177     }
178
179     /* Close main window and display */
180     X11DestroyWindow( p_intf );
181     XCloseDisplay( p_intf->p_sys->p_display );
182
183     /* Destroy structure */
184     free( p_intf->p_sys );
185 }
186
187
188 /*****************************************************************************
189  * intf_X11Manage: event loop
190  *****************************************************************************/
191 void intf_X11Manage( intf_thread_t *p_intf )
192 {
193     /* Manage main window */
194     X11ManageWindow( p_intf );
195 }
196
197 /* following functions are local */
198
199 /*****************************************************************************
200  * X11CreateWindow: open and set-up X11 main window
201  *****************************************************************************/
202 static int X11CreateWindow( intf_thread_t *p_intf )
203 {
204     XSizeHints              xsize_hints;
205     XSetWindowAttributes    xwindow_attributes;
206     XGCValues               xgcvalues;
207     XEvent                  xevent;
208     boolean_t               b_expose;
209     boolean_t               b_configure_notify;
210     boolean_t               b_map_notify;
211
212     /* Set main window's size */
213     p_intf->p_sys->i_width =  main_GetIntVariable( VOUT_WIDTH_VAR,
214                                                    VOUT_WIDTH_DEFAULT );
215     p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
216                                                    VOUT_HEIGHT_DEFAULT );
217
218     /* Prepare window manager hints and properties */
219     xsize_hints.base_width =            p_intf->p_sys->i_width;
220     xsize_hints.base_height =           p_intf->p_sys->i_height;
221     xsize_hints.flags =                 PSize;
222     p_intf->p_sys->wm_protocols =       XInternAtom( p_intf->p_sys->p_display,
223                                                      "WM_PROTOCOLS", True );
224     p_intf->p_sys->wm_delete_window =   XInternAtom( p_intf->p_sys->p_display,
225                                                      "WM_DELETE_WINDOW", True );
226
227     /* Prepare window attributes */
228     xwindow_attributes.backing_store = Always;       /* save the hidden part */
229     xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
230                                                       p_intf->p_sys->i_screen );
231
232     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
233
234     /* Create the window and set hints - the window must receive ConfigureNotify
235      * events, and, until it is displayed, Expose and MapNotify events. */
236     p_intf->p_sys->window =
237             XCreateWindow( p_intf->p_sys->p_display,
238                            DefaultRootWindow( p_intf->p_sys->p_display ),
239                            0, 0,
240                            p_intf->p_sys->i_width, p_intf->p_sys->i_height, 1,
241                            0, InputOutput, 0,
242                            CWBackingStore | CWBackPixel | CWEventMask,
243                            &xwindow_attributes );
244
245     /* Set window manager hints and properties: size hints, command,
246      * window's name, and accepted protocols */
247     XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window,
248                        &xsize_hints );
249     XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
250                  p_main->ppsz_argv, p_main->i_argc );
251     XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
252
253     if( (p_intf->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
254         || (p_intf->p_sys->wm_delete_window == None)
255         || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
256                              &p_intf->p_sys->wm_delete_window, 1 ) )
257     {
258         /* WM_DELETE_WINDOW is not supported by window manager */
259         intf_Msg("intf error: missing or bad window manager - please exit program kindly.");
260     }
261
262     /* Creation of a graphic context that doesn't generate a GraphicsExpose
263      * event when using functions like XCopyArea */
264     xgcvalues.graphics_exposures = False;
265     p_intf->p_sys->gc =  XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
266                                     GCGraphicsExposures, &xgcvalues);
267
268     /* Send orders to server, and wait until window is displayed - three
269      * events must be received: a MapNotify event, an Expose event allowing
270      * drawing in the window, and a ConfigureNotify to get the window
271      * dimensions. Once those events have been received, only ConfigureNotify
272      * events need to be received. */
273     b_expose = 0;
274     b_configure_notify = 0;
275     b_map_notify = 0;
276     XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
277     do
278     {
279         XNextEvent( p_intf->p_sys->p_display, &xevent);
280         if( (xevent.type == Expose)
281             && (xevent.xexpose.window == p_intf->p_sys->window) )
282         {
283             b_expose = 1;
284         }
285         else if( (xevent.type == MapNotify)
286                  && (xevent.xmap.window == p_intf->p_sys->window) )
287         {
288             b_map_notify = 1;
289         }
290         else if( (xevent.type == ConfigureNotify)
291                  && (xevent.xconfigure.window == p_intf->p_sys->window) )
292         {
293             b_configure_notify = 1;
294             p_intf->p_sys->i_width = xevent.xconfigure.width;
295             p_intf->p_sys->i_height = xevent.xconfigure.height;
296         }
297     } while( !( b_expose && b_configure_notify && b_map_notify ) );
298
299     XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
300                   StructureNotifyMask | KeyPressMask | ButtonPressMask );
301
302     if( XDefaultDepth(p_intf->p_sys->p_display, p_intf->p_sys->i_screen) == 8 )
303     {
304         /* Allocate a new palette */
305         p_intf->p_sys->colormap = XCreateColormap( p_intf->p_sys->p_display,
306                               DefaultRootWindow( p_intf->p_sys->p_display ),
307                               DefaultVisual( p_intf->p_sys->p_display,
308                                              p_intf->p_sys->i_screen ),
309                               AllocAll );
310
311         xwindow_attributes.colormap = p_intf->p_sys->colormap;
312         XChangeWindowAttributes( p_intf->p_sys->p_display,
313                                  p_intf->p_sys->window,
314                                  CWColormap, &xwindow_attributes );
315     }
316
317     /* At this stage, the window is open, displayed, and ready to receive data */
318     return( 0 );
319 }
320
321 /*****************************************************************************
322  * X11DestroyWindow: destroy X11 main window
323  *****************************************************************************/
324 static void X11DestroyWindow( intf_thread_t *p_intf )
325 {
326     XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
327     XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
328     XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
329 }
330
331 /*****************************************************************************
332  * X11ManageWindow: manage X11 main window
333  *****************************************************************************/
334 static void X11ManageWindow( intf_thread_t *p_intf )
335 {
336     XEvent      xevent;                                         /* X11 event */
337     boolean_t   b_resized;                        /* window has been resized */
338     char        i_key;                                    /* ISO Latin-1 key */
339
340     /* Handle X11 events: ConfigureNotify events are parsed to know if the
341      * output window's size changed, MapNotify and UnmapNotify to know if the
342      * window is mapped (and if the display is useful), and ClientMessages
343      * to intercept window destruction requests */
344     b_resized = 0;
345     while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
346                               StructureNotifyMask | KeyPressMask |
347                               ButtonPressMask, &xevent ) == True )
348     {
349         /* ConfigureNotify event: prepare  */
350         if( (xevent.type == ConfigureNotify)
351             && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
352                 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
353         {
354             /* Update dimensions */
355             b_resized = 1;
356             p_intf->p_sys->i_width = xevent.xconfigure.width;
357             p_intf->p_sys->i_height = xevent.xconfigure.height;
358         }
359         /* MapNotify event: change window status and disable screen saver */
360         else if( xevent.type == MapNotify)
361         {
362             if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
363             {
364                 X11DisableScreenSaver( p_intf );
365                 p_intf->p_vout->b_active = 1;
366             }
367         }
368         /* UnmapNotify event: change window status and enable screen saver */
369         else if( xevent.type == UnmapNotify )
370         {
371             if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
372             {
373                 X11EnableScreenSaver( p_intf );
374                 p_intf->p_vout->b_active = 0;
375             }
376         }
377         /* Keyboard event */
378         else if( xevent.type == KeyPress )
379         {
380             if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
381             {
382                 if( intf_ProcessKey( p_intf, i_key ) )
383                 {
384                     intf_DbgMsg("unhandled key '%c' (%i)", (char) i_key, i_key );
385                 }
386             }
387         }
388         /* Mouse click */
389         else if( xevent.type == ButtonPress )
390         {
391             switch( ((XButtonEvent *)&xevent)->button )
392             {
393                 case Button1:
394                     /* in this part we will eventually manage
395                      * clicks for DVD navigation for instance */
396                     break;
397
398                 case Button2:
399                     X11TogglePointer( p_intf );
400                     break;
401
402                 case Button3:
403                     vlc_mutex_lock( &p_intf->p_vout->change_lock );
404                     p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
405                     p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
406                     vlc_mutex_unlock( &p_intf->p_vout->change_lock );
407                     break;
408             }
409         }
410 #ifdef DEBUG
411         /* Other event */
412         else
413         {
414             intf_DbgMsg( "%p -> unhandled event type %d received",
415                          p_intf, xevent.type );
416         }
417 #endif
418     }
419
420     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
421      * are handled - according to the man pages, the format is always 32
422      * in this case */
423     while( XCheckTypedEvent( p_intf->p_sys->p_display,
424                              ClientMessage, &xevent ) )
425     {
426         if( (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
427             && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
428         {
429             p_intf->b_die = 1;
430         }
431         else
432         {
433             intf_DbgMsg( "%p -> unhandled ClientMessage received", p_intf );
434         }
435     }
436
437     /*
438      * Handle vout or interface windows resizing
439      */
440     if( p_intf->p_vout != NULL )
441     {
442         if( b_resized )
443         {
444             /* If interface window has been resized, change vout size */
445             intf_DbgMsg( "resizing output window" );
446             vlc_mutex_lock( &p_intf->p_vout->change_lock );
447             p_intf->p_vout->i_width =  p_intf->p_sys->i_width;
448             p_intf->p_vout->i_height = p_intf->p_sys->i_height;
449             p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
450             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
451         }
452         else if( (p_intf->p_vout->i_width  != p_intf->p_sys->i_width) ||
453                  (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
454         {
455            /* If video output size has changed, change interface window size */
456             intf_DbgMsg( "resizing output window" );
457             p_intf->p_sys->i_width =    p_intf->p_vout->i_width;
458             p_intf->p_sys->i_height =   p_intf->p_vout->i_height;
459             XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
460                            p_intf->p_sys->i_width, p_intf->p_sys->i_height );
461         }
462     }
463 }
464
465 /*****************************************************************************
466  * X11EnableScreenSaver: enable screen saver
467  *****************************************************************************
468  * This function enable the screen saver on a display after it had been
469  * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
470  * know wether the screen saver can be activated or not: if n successive calls
471  * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
472  * will be required before the screen saver could effectively be activated.
473  *****************************************************************************/
474 void X11EnableScreenSaver( intf_thread_t *p_intf )
475 {
476     if( p_intf->p_sys->i_ss_count++ == 0 )
477     {
478         intf_DbgMsg( "intf: enabling screen saver" );
479         XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
480                          p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
481                          p_intf->p_sys->i_ss_exposure );
482     }
483 }
484
485 /*****************************************************************************
486  * X11DisableScreenSaver: disable screen saver
487  *****************************************************************************
488  * See XEnableScreenSaver
489  *****************************************************************************/
490 void X11DisableScreenSaver( intf_thread_t *p_intf )
491 {
492     if( --p_intf->p_sys->i_ss_count == 0 )
493     {
494         /* Save screen saver informations */
495         XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
496                          &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
497                          &p_intf->p_sys->i_ss_exposure );
498
499         /* Disable screen saver */
500         intf_DbgMsg("intf: disabling screen saver");
501         XSetScreenSaver( p_intf->p_sys->p_display, 0,
502                          p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
503                          p_intf->p_sys->i_ss_exposure );
504     }
505 }
506
507 /*****************************************************************************
508  * X11TogglePointer: hide or show the mouse pointer
509  *****************************************************************************
510  * This function hides the X pointer if it is visible by putting it at
511  * coordinates (32,32) and setting the pointer sprite to a blank one. To
512  * show it again, we disable the sprite and restore the original coordinates.
513  *****************************************************************************/
514 void X11TogglePointer( intf_thread_t *p_intf )
515 {
516     static Cursor cursor;
517     static boolean_t b_cursor = 0;
518
519     if( p_intf->p_sys->b_mouse )
520     {
521         p_intf->p_sys->b_mouse = 0;
522
523         if( !b_cursor )
524         {
525             XColor color;
526             Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
527                                DefaultRootWindow(p_intf->p_sys->p_display),
528                                1, 1, 1 );
529
530             XParseColor( p_intf->p_sys->p_display,
531                          XCreateColormap( p_intf->p_sys->p_display,
532                                           DefaultRootWindow(
533                                                   p_intf->p_sys->p_display ),
534                                           DefaultVisual(
535                                                   p_intf->p_sys->p_display,
536                                                   p_intf->p_sys->i_screen ),
537                                           AllocNone ),
538                          "black", &color );
539
540             cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
541                            blank, blank, &color, &color, 1, 1 );
542
543             b_cursor = 1;
544         }
545         XDefineCursor( p_intf->p_sys->p_display,
546                        p_intf->p_sys->window, cursor );
547     }
548     else
549     {
550         p_intf->p_sys->b_mouse = 1;
551
552         XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );
553     }
554 }