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